import 'dart:developer'; import 'package:enaklo_pos/core/components/flushbar.dart'; import 'package:enaklo_pos/core/components/buttons.dart'; import 'package:enaklo_pos/data/repositories/product/product_repository.dart'; import 'package:enaklo_pos/data/repositories/category/category_repository.dart'; import 'package:enaklo_pos/data/datasources/product/product_local_datasource.dart'; import 'package:enaklo_pos/data/datasources/category/category_local_datasource.dart'; import 'package:enaklo_pos/presentation/setting/widgets/settings_title.dart'; import 'package:flutter/material.dart'; class SettingSyncPage extends StatefulWidget { const SettingSyncPage({super.key}); @override State createState() => _SettingSyncPageState(); } class _SettingSyncPageState extends State { final ProductRepository _productRepository = ProductRepository.instance; final CategoryRepository _categoryRepository = CategoryRepository.instance; final ProductLocalDatasource _productLocalDatasource = ProductLocalDatasource.instance; final CategoryLocalDatasource _categoryLocalDatasource = CategoryLocalDatasource.instance; bool _isLoading = false; bool _isSyncing = false; Map _productStats = {}; Map _categoryStats = {}; @override void initState() { super.initState(); _loadStats(); } Future _loadStats() async { setState(() => _isLoading = true); try { final productStats = await _productRepository.getDatabaseStats(); final categoryStats = await _categoryRepository.getDatabaseStats(); setState(() { _productStats = productStats; _categoryStats = categoryStats; _isLoading = false; }); } catch (e) { log('Error loading stats: $e'); setState(() => _isLoading = false); } } Future _syncAllData() async { setState(() => _isSyncing = true); try { // Show loading dialog showDialog( context: context, barrierDismissible: false, builder: (context) => AlertDialog( content: Column( mainAxisSize: MainAxisSize.min, children: [ CircularProgressIndicator(), SizedBox(height: 16), Text('Sinkronisasi semua data...'), ], ), ), ); // Sync categories first final categoryResult = await _categoryRepository.syncAllCategories(); await categoryResult.fold( (error) async { Navigator.of(context).pop(); AppFlushbar.showError(context, 'Gagal sync kategori: $error'); return; }, (success) async { log('Categories synced successfully'); }, ); // Sync products after categories final productResult = await _productRepository.syncAllProducts(); await productResult.fold( (error) async { Navigator.of(context).pop(); AppFlushbar.showError(context, 'Gagal sync produk: $error'); return; }, (success) async { log('Products synced successfully'); }, ); Navigator.of(context).pop(); AppFlushbar.showSuccess(context, 'Sinkronisasi berhasil'); _loadStats(); // Refresh stats } catch (e) { Navigator.of(context).pop(); AppFlushbar.showError(context, 'Gagal sinkronisasi: $e'); } finally { setState(() => _isSyncing = false); } } Future _clearAllData() async { // Show confirmation dialog final confirmed = await showDialog( context: context, builder: (context) => AlertDialog( title: Text('Hapus Semua Data'), content: Text( 'Apakah Anda yakin ingin menghapus semua data lokal? Tindakan ini tidak dapat dibatalkan.'), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(false), child: Text('Batal'), ), TextButton( onPressed: () => Navigator.of(context).pop(true), child: Text('Hapus', style: TextStyle(color: Colors.red)), ), ], ), ); if (confirmed != true) return; setState(() => _isLoading = true); try { // Clear products and categories using datasource await _productLocalDatasource.clearAllProducts(); await _categoryLocalDatasource.clearAllCategories(); // Clear caches _productRepository.clearCache(); _categoryRepository.clearCache(); AppFlushbar.showSuccess(context, 'Semua data berhasil dihapus'); _loadStats(); // Refresh stats } catch (e) { AppFlushbar.showError(context, 'Gagal menghapus data: $e'); } finally { setState(() => _isLoading = false); } } Future _syncProducts() async { setState(() => _isLoading = true); try { final result = await _productRepository.syncAllProducts(); await result.fold( (error) async { AppFlushbar.showError(context, 'Gagal sync produk: $error'); }, (success) async { AppFlushbar.showSuccess(context, success); _loadStats(); // Refresh stats }, ); } catch (e) { AppFlushbar.showError(context, 'Gagal sync produk: $e'); } finally { setState(() => _isLoading = false); } } Future _syncCategories() async { setState(() => _isLoading = true); try { final result = await _categoryRepository.syncAllCategories(); await result.fold( (error) async { AppFlushbar.showError(context, 'Gagal sync kategori: $error'); }, (success) async { AppFlushbar.showSuccess(context, success); _loadStats(); // Refresh stats }, ); } catch (e) { AppFlushbar.showError(context, 'Gagal sync kategori: $e'); } finally { setState(() => _isLoading = false); } } @override Widget build(BuildContext context) { return SingleChildScrollView( padding: EdgeInsets.symmetric(horizontal: 16), child: Column( children: [ SettingsTitle( 'Sinkronisasi', subtitle: 'Sinkronisasi data dengan server', ), SizedBox(height: 24), // Quick Actions _buildQuickActions(), SizedBox(height: 24), // Sync Tables _buildSyncTables(), SizedBox(height: 24), // Database Stats _buildDatabaseStats(), ], ), ); } Widget _buildQuickActions() { return Container( padding: EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.grey.shade200), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Aksi Cepat', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: Colors.grey.shade800, ), ), SizedBox(height: 16), Row( children: [ Expanded( child: Button.filled( onPressed: _isSyncing || _isLoading ? null : _syncAllData, label: _isSyncing ? 'Menyinkronkan...' : 'Sync Semua Data', ), ), SizedBox(width: 12), Expanded( child: Button.outlined( onPressed: _isLoading || _isSyncing ? null : _clearAllData, label: 'Hapus Semua Data', textColor: Colors.red, ), ), ], ), ], ), ); } Widget _buildSyncTables() { return Container( padding: EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.grey.shade200), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Sinkronisasi per Tabel', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: Colors.grey.shade800, ), ), SizedBox(height: 16), // Categories Sync _buildSyncTableItem( title: 'Kategori', subtitle: 'Sinkronkan data kategori produk', icon: Icons.category, color: Colors.blue, count: _categoryStats['total_categories'] ?? 0, onSync: _syncCategories, onClear: () async { final confirmed = await _showClearConfirmation('kategori'); if (confirmed) { await _categoryLocalDatasource.clearAllCategories(); _categoryRepository.clearCache(); AppFlushbar.showSuccess( context, 'Data kategori berhasil dihapus'); _loadStats(); } }, ), SizedBox(height: 12), Divider(), SizedBox(height: 12), // Products Sync _buildSyncTableItem( title: 'Produk', subtitle: 'Sinkronkan data produk dan variant', icon: Icons.inventory_2, color: Colors.green, count: _productStats['total_products'] ?? 0, onSync: _syncProducts, onClear: () async { final confirmed = await _showClearConfirmation('produk'); if (confirmed) { await _productLocalDatasource.clearAllProducts(); _productRepository.clearCache(); AppFlushbar.showSuccess( context, 'Data produk berhasil dihapus'); _loadStats(); } }, ), ], ), ); } Widget _buildSyncTableItem({ required String title, required String subtitle, required IconData icon, required Color color, required int count, required VoidCallback onSync, required VoidCallback onClear, }) { return Row( children: [ // Icon and info Container( padding: EdgeInsets.all(12), decoration: BoxDecoration( color: color.withOpacity(0.1), borderRadius: BorderRadius.circular(8), ), child: Icon(icon, color: color, size: 24), ), SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Text( title, style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, ), ), SizedBox(width: 8), Container( padding: EdgeInsets.symmetric(horizontal: 6, vertical: 2), decoration: BoxDecoration( color: color.withOpacity(0.1), borderRadius: BorderRadius.circular(4), ), child: Text( '$count', style: TextStyle( fontSize: 11, fontWeight: FontWeight.w600, color: color, ), ), ), ], ), Text( subtitle, style: TextStyle( fontSize: 12, color: Colors.grey.shade600, ), ), ], ), ), // Actions Row( mainAxisSize: MainAxisSize.min, children: [ IconButton( onPressed: _isLoading || _isSyncing ? null : onSync, icon: Icon(Icons.sync, size: 20), tooltip: 'Sync $title', style: IconButton.styleFrom( backgroundColor: color.withOpacity(0.1), foregroundColor: color, ), ), SizedBox(width: 4), IconButton( onPressed: _isLoading || _isSyncing ? null : onClear, icon: Icon(Icons.delete_outline, size: 20), tooltip: 'Hapus $title', style: IconButton.styleFrom( backgroundColor: Colors.red.withOpacity(0.1), foregroundColor: Colors.red, ), ), ], ), ], ); } Widget _buildDatabaseStats() { return Container( padding: EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.grey.shade200), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Text( 'Statistik Database', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: Colors.grey.shade800, ), ), Spacer(), if (_isLoading) SizedBox( width: 16, height: 16, child: CircularProgressIndicator(strokeWidth: 2), ) else IconButton( onPressed: _loadStats, icon: Icon(Icons.refresh, size: 20), tooltip: 'Refresh Stats', ), ], ), SizedBox(height: 16), if (_isLoading) Center( child: Padding( padding: EdgeInsets.symmetric(vertical: 20), child: CircularProgressIndicator(), ), ) else Column( children: [ // Category stats _buildStatRow( 'Kategori', _categoryStats['total_categories']?.toString() ?? '0', Icons.category, Colors.blue, ), SizedBox(height: 8), // Product stats _buildStatRow( 'Produk', _productStats['total_products']?.toString() ?? '0', Icons.inventory_2, Colors.green, ), SizedBox(height: 8), // Variant stats _buildStatRow( 'Variant', _productStats['total_variants']?.toString() ?? '0', Icons.tune, Colors.orange, ), SizedBox(height: 8), // Cache stats _buildStatRow( 'Cache Entries', '${(_productStats['cache_entries'] ?? 0) + (_categoryStats['cache_entries'] ?? 0)}', Icons.memory, Colors.purple, ), SizedBox(height: 8), // Database size _buildStatRow( 'Ukuran Database', '${((_productStats['database_size_mb'] ?? 0.0) + (_categoryStats['database_size_mb'] ?? 0.0)).toStringAsFixed(2)} MB', Icons.storage, Colors.grey.shade600, ), ], ), ], ), ); } Widget _buildStatRow(String label, String value, IconData icon, Color color) { return Row( children: [ Icon(icon, size: 16, color: color), SizedBox(width: 8), Expanded( child: Text( label, style: TextStyle( fontSize: 14, color: Colors.grey.shade700, ), ), ), Text( value, style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, color: color, ), ), ], ); } Future _showClearConfirmation(String dataType) async { return await showDialog( context: context, builder: (context) => AlertDialog( title: Text('Hapus Data $dataType'), content: Text('Apakah Anda yakin ingin menghapus semua data $dataType?'), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(false), child: Text('Batal'), ), TextButton( onPressed: () => Navigator.of(context).pop(true), child: Text('Hapus', style: TextStyle(color: Colors.red)), ), ], ), ) ?? false; } }