import 'dart:io'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:excel/excel.dart' hide Border; import 'package:open_file/open_file.dart'; import 'package:path_provider/path_provider.dart'; import '../services/supabase_service.dart'; import '../services/mqtt_service.dart'; import 'package:mqtt_client/mqtt_client.dart'; import '../constants/app_colors.dart'; // <-- IMPORT APP COLORS DITAMBAHKAN class SettingsScreen extends StatefulWidget { const SettingsScreen({super.key}); @override State createState() => _SettingsScreenState(); } class _SettingsScreenState extends State { String _namaLengkap = ''; bool _isLoadingProfile = true; bool _isExporting = false; @override void initState() { super.initState(); _loadProfile(); } Future _loadProfile() async { try { final profile = await SupabaseService().getProfile(); if (mounted) { setState(() { _namaLengkap = profile?['nama_lengkap'] ?? ''; _isLoadingProfile = false; }); } } catch (e) { if (mounted) setState(() => _isLoadingProfile = false); } } // ── Export foto_dataset ke Excel (Warna Excel Tetap Dipertahankan) ── Future _exportExcel() async { setState(() => _isExporting = true); try { // Ambil semua data dari foto_dataset final data = await SupabaseService().getFotoDataset(); if (data.isEmpty) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: const Text('Belum ada data dataset untuk diexport!'), // --- BERUBAH: Warna Notifikasi Peringatan --- backgroundColor: AppColors.warning, ), ); } return; } // Buat file Excel final excel = Excel.createExcel(); final sheet = excel['Dataset Kopi']; // Header kolom final headers = [ 'No', 'Timestamp', 'Suhu (°C)', 'Kelembapan (%)', 'Intensitas Cahaya (Lux)', 'Kipas 1 (Intake)', 'Kipas 2 (Exhaust)', ]; // Style header (Dibiarkan tetap hijau standar Excel) for (int i = 0; i < headers.length; i++) { final cell = sheet.cell( CellIndex.indexByColumnRow(columnIndex: i, rowIndex: 0)); cell.value = TextCellValue(headers[i]); cell.cellStyle = CellStyle( bold: true, backgroundColorHex: ExcelColor.fromHexString('#16A34A'), fontColorHex: ExcelColor.fromHexString('#FFFFFF'), ); } // Isi data — tanpa URL Foto for (int i = 0; i < data.length; i++) { final row = data[i]; final rowIndex = i + 1; sheet.cell(CellIndex.indexByColumnRow(columnIndex: 0, rowIndex: rowIndex)) .value = IntCellValue(i + 1); sheet.cell(CellIndex.indexByColumnRow(columnIndex: 1, rowIndex: rowIndex)) .value = TextCellValue(row['timestamp']?.toString() ?? '-'); sheet.cell(CellIndex.indexByColumnRow(columnIndex: 2, rowIndex: rowIndex)) .value = DoubleCellValue(double.tryParse(row['suhu'].toString()) ?? 0); sheet.cell(CellIndex.indexByColumnRow(columnIndex: 3, rowIndex: rowIndex)) .value = DoubleCellValue(double.tryParse(row['kelembapan'].toString()) ?? 0); sheet.cell(CellIndex.indexByColumnRow(columnIndex: 4, rowIndex: rowIndex)) .value = DoubleCellValue(double.tryParse(row['intensitas'].toString()) ?? 0); sheet.cell(CellIndex.indexByColumnRow(columnIndex: 5, rowIndex: rowIndex)) .value = TextCellValue(row['kipas1']?.toString() ?? '-'); sheet.cell(CellIndex.indexByColumnRow(columnIndex: 6, rowIndex: rowIndex)) .value = TextCellValue(row['kipas2']?.toString() ?? '-'); } // Set lebar kolom — tanpa kolom URL sheet.setColumnWidth(0, 5); sheet.setColumnWidth(1, 22); sheet.setColumnWidth(2, 12); sheet.setColumnWidth(3, 16); sheet.setColumnWidth(4, 24); sheet.setColumnWidth(5, 18); sheet.setColumnWidth(6, 18); // Simpan file final dir = await getApplicationDocumentsDirectory(); final tanggal = DateTime.now() .toString() .substring(0, 10) .replaceAll('-', ''); final fileName = 'dataset_kopi_$tanggal.xlsx'; final filePath = '${dir.path}/$fileName'; final fileBytes = excel.save(); if (fileBytes == null) throw Exception('Gagal generate file Excel'); final file = File(filePath); await file.writeAsBytes(fileBytes); // Share file await OpenFile.open(filePath); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( '✓ Export berhasil — ${data.length} data tersimpan'), // --- BERUBAH: Warna Notifikasi Sukses --- backgroundColor: AppColors.success, ), ); } } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Gagal export: $e'), // --- BERUBAH: Warna Notifikasi Error --- backgroundColor: AppColors.error, ), ); } } finally { if (mounted) setState(() => _isExporting = false); } } @override Widget build(BuildContext context) { final mqtt = Provider.of(context); final user = SupabaseService().currentUser; return Scaffold( // --- BERUBAH: Background UI Utama --- backgroundColor: AppColors.background, body: SafeArea( child: SingleChildScrollView( child: Padding( padding: const EdgeInsets.all(20.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // ── Profile Section ── Container( padding: const EdgeInsets.all(15), decoration: BoxDecoration( // --- BERUBAH: Background Kartu Profil --- color: AppColors.cardBg, borderRadius: BorderRadius.circular(20), // --- BERUBAH: Garis pinggir halus --- border: Border.all(color: AppColors.textSecondary.withValues(alpha: 0.15)), ), child: Row( children: [ const CircleAvatar( radius: 30, // --- BERUBAH: Warna Avatar --- backgroundColor: AppColors.primary, child: Icon(Icons.person, color: Colors.white, size: 40), ), const SizedBox(width: 15), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _isLoadingProfile ? const SizedBox( width: 100, height: 16, child: LinearProgressIndicator( // --- BERUBAH: Warna Loading --- color: AppColors.primary, backgroundColor: AppColors.background, ), ) : Text( _namaLengkap.isNotEmpty ? _namaLengkap : 'Admin Kopi', style: const TextStyle( // --- BERUBAH: Warna Nama Profil --- color: AppColors.textMain, fontSize: 18, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 4), Text( user?.email ?? 'admin@coffee.io', style: const TextStyle( // --- BERUBAH: Warna Email Profil --- color: AppColors.textSecondary, fontSize: 12, fontWeight: FontWeight.bold, ), ), const Text( 'Status: Active User', style: TextStyle( // --- BERUBAH: Warna Status Profil --- color: AppColors.success, fontSize: 12, fontWeight: FontWeight.bold, ), ), ], ), ], ), ), const SizedBox(height: 30), // ── Sistem & Koneksi ── _buildSectionTitle('Sistem & Koneksi'), _buildSettingsCard([ _buildConnectionStatus( 'Database Supabase', true, Icons.storage), // --- BERUBAH: Warna Garis Pemisah Menu --- Divider(color: AppColors.textSecondary.withValues(alpha: 0.2), height: 1), _buildConnectionStatus( 'MQTT Broker (EMQX)', mqtt.client?.connectionStatus?.state == MqttConnectionState.connected, Icons.cloud_sync, ), ]), const SizedBox(height: 20), // ── Manajemen Data ── _buildSectionTitle('Manajemen Data'), _buildSettingsCard([ _isExporting ? const ListTile( leading: SizedBox( width: 22, height: 22, child: CircularProgressIndicator( // --- BERUBAH: Warna Loading --- color: AppColors.primary, strokeWidth: 2), ), title: Text( 'Menyiapkan file Excel...', style: TextStyle( // --- BERUBAH: Warna Teks Loading --- color: AppColors.textSecondary, fontSize: 14), ), ) : _buildSettingItem( Icons.table_chart, 'Export Data Dataset (.xlsx)', _exportExcel, ), ]), const SizedBox(height: 20), // ── Keamanan ── _buildSectionTitle('Keamanan'), _buildSettingsCard([ _buildSettingItem( Icons.lock_reset, 'Ganti Password Akun', () { Navigator.pushNamed( context, '/change_password', arguments: {'dari_reset': false}, ); }), ]), const SizedBox(height: 30), // ── Tombol Keluar ── SizedBox( width: double.infinity, height: 55, child: ElevatedButton.icon( onPressed: () async { final konfirmasi = await showDialog( context: context, builder: (ctx) => AlertDialog( // --- BERUBAH: Latar Belakang Dialog --- backgroundColor: AppColors.cardBg, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)), title: const Row( children: [ Icon(Icons.logout, color: AppColors.error), SizedBox(width: 10), Text('Keluar dari Akun', // --- BERUBAH: Warna Judul Dialog --- style: TextStyle(color: AppColors.textMain, fontSize: 18)), ], ), content: const Text( 'Apakah kamu yakin ingin keluar dari akun ini?', // --- BERUBAH: Warna Teks Konten Dialog --- style: TextStyle(color: AppColors.textSecondary), ), actions: [ TextButton( onPressed: () => Navigator.pop(ctx, false), child: const Text('Batal', // --- BERUBAH: Warna Tombol Batal --- style: TextStyle(color: AppColors.primary)), ), ElevatedButton( onPressed: () => Navigator.pop(ctx, true), style: ElevatedButton.styleFrom( // --- BERUBAH: Warna Tombol Keluar di Dialog --- backgroundColor: AppColors.error, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10)), ), child: const Text('Keluar', style: TextStyle(color: Colors.white)), ), ], ), ); if (konfirmasi == true) { await SupabaseService().signOut(); if (context.mounted) { Navigator.pushReplacementNamed(context, '/login'); } } }, style: ElevatedButton.styleFrom( // --- BERUBAH: Warna Latar Tombol Keluar --- backgroundColor: AppColors.error.withValues(alpha: 0.8), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(15), ), ), icon: const Icon(Icons.logout, color: Colors.white), // Tetap putih label: const Text( 'Keluar dari Akun', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: Colors.white), // Tetap putih ), ), ), const SizedBox(height: 100), ], ), ), ), ), ); } Widget _buildSectionTitle(String title) { return Padding( padding: const EdgeInsets.only(bottom: 10, left: 5), child: Text( title, style: const TextStyle( // --- BERUBAH: Warna Judul Seksi/Kategori --- color: AppColors.textSecondary, fontSize: 13, fontWeight: FontWeight.bold), ), ); } Widget _buildSettingsCard(List children) { return Container( decoration: BoxDecoration( // --- BERUBAH: Latar Belakang Kartu Pengaturan --- color: AppColors.cardBg, borderRadius: BorderRadius.circular(20), // --- BERUBAH: Garis pinggir halus --- border: Border.all(color: AppColors.textSecondary.withValues(alpha: 0.15)), ), child: Column(children: children), ); } Widget _buildSettingItem(IconData icon, String title, VoidCallback? onTap, {String? trailing}) { return ListTile( // --- BERUBAH: Warna Ikon Leading --- leading: Icon(icon, color: AppColors.primary, size: 22), title: Text(title, // --- BERUBAH: Warna Teks Menu --- style: const TextStyle(color: AppColors.textMain, fontSize: 14)), trailing: trailing != null ? Text(trailing, // --- BERUBAH: Warna Teks Trailing --- style: const TextStyle(color: AppColors.textSecondary, fontSize: 12)) // --- BERUBAH: Warna Ikon Panah Kanan --- : const Icon(Icons.chevron_right, color: AppColors.textSecondary, size: 18), onTap: onTap, ); } Widget _buildConnectionStatus( String title, bool isConnected, IconData icon) { return ListTile( // --- BERUBAH: Warna Ikon Status Database/MQTT --- leading: Icon(icon, color: isConnected ? AppColors.info : AppColors.error, size: 22), title: Text(title, // --- BERUBAH: Warna Teks Nama Layanan --- style: const TextStyle(color: AppColors.textMain, fontSize: 14)), trailing: Row( mainAxisSize: MainAxisSize.min, children: [ Container( width: 8, height: 8, decoration: BoxDecoration( shape: BoxShape.circle, // --- BERUBAH: Warna Titik Indikator --- color: isConnected ? AppColors.success : AppColors.error, ), ), const SizedBox(width: 8), Text( isConnected ? 'Online' : 'Offline', style: TextStyle( // --- BERUBAH: Warna Teks Online/Offline --- color: isConnected ? AppColors.success : AppColors.error, fontSize: 12, fontWeight: FontWeight.bold), ), ], ), ); } }