import 'dart:async'; import 'package:enaklo_pos/core/extensions/build_context_ext.dart'; import 'package:enaklo_pos/presentation/home/pages/dashboard_page.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../core/components/buttons.dart'; import '../../../core/constants/colors.dart'; import '../bloc/data_sync_bloc.dart'; class DataSyncPage extends StatefulWidget { const DataSyncPage({super.key}); @override State createState() => _DataSyncPageState(); } class _DataSyncPageState extends State with TickerProviderStateMixin { late AnimationController _animationController; late Animation _progressAnimation; @override void initState() { super.initState(); _animationController = AnimationController( duration: Duration(milliseconds: 500), vsync: this, ); _progressAnimation = Tween( begin: 0.0, end: 1.0, ).animate(CurvedAnimation( parent: _animationController, curve: Curves.easeInOut, )); // Auto start sync WidgetsBinding.instance.addPostFrameCallback((_) { context.read().add(const DataSyncEvent.startSync()); }); } @override void dispose() { _animationController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final mediaQuery = MediaQuery.of(context); final isLandscape = mediaQuery.orientation == Orientation.landscape; final screenHeight = mediaQuery.size.height; final screenWidth = mediaQuery.size.width; return Scaffold( backgroundColor: Colors.grey.shade50, body: SafeArea( child: BlocConsumer( listener: (context, state) { state.maybeWhen( orElse: () {}, syncing: (step, progress, message) { _animationController.animateTo(progress); }, completed: (stats) { _animationController.animateTo(1.0); // Navigate to home after delay Future.delayed(Duration(seconds: 2), () { context.pushReplacement(DashboardPage()); }); }, error: (message) { _animationController.stop(); }, ); }, builder: (context, state) { if (isLandscape) { return _buildLandscapeLayout(state, screenWidth, screenHeight); } else { return _buildPortraitLayout(state, screenHeight); } }, ), ), ); } // Portrait layout Widget _buildPortraitLayout(DataSyncState state, double screenHeight) { return Padding( padding: EdgeInsets.all(24), child: Column( children: [ SizedBox(height: screenHeight * 0.08), _buildHeader(false), SizedBox(height: screenHeight * 0.08), Expanded( child: state.when( initial: () => _buildInitialState(false), syncing: (step, progress, message) => _buildSyncingState(step, progress, message, false), completed: (stats) => _buildCompletedState(stats, false), error: (message) => _buildErrorState(message, false), ), ), SizedBox(height: 20), _buildActions(state), ], ), ); } // Landscape layout Widget _buildLandscapeLayout( DataSyncState state, double screenWidth, double screenHeight) { return Padding( padding: EdgeInsets.symmetric(horizontal: 32, vertical: 16), child: Row( children: [ Expanded( flex: 2, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ _buildHeader(true), SizedBox(height: 20), _buildActions(state), ], ), ), SizedBox(width: 40), Expanded( flex: 3, child: Container( height: screenHeight * 0.8, child: state.when( initial: () => _buildInitialState(true), syncing: (step, progress, message) => _buildSyncingState(step, progress, message, true), completed: (stats) => _buildCompletedState(stats, true), error: (message) => _buildErrorState(message, true), ), ), ), ], ), ); } Widget _buildHeader(bool isLandscape) { return Column( children: [ Container( width: isLandscape ? 60 : 80, height: isLandscape ? 60 : 80, decoration: BoxDecoration( color: AppColors.primary.withOpacity(0.1), borderRadius: BorderRadius.circular(isLandscape ? 15 : 20), ), child: Icon( Icons.sync, size: isLandscape ? 30 : 40, color: AppColors.primary, ), ), SizedBox(height: isLandscape ? 12 : 20), Text( 'Sinkronisasi Data', style: TextStyle( fontSize: isLandscape ? 20 : 24, fontWeight: FontWeight.bold, color: Colors.grey.shade800, ), ), SizedBox(height: isLandscape ? 4 : 8), Text( 'Mengunduh kategori dan produk terbaru', style: TextStyle( fontSize: isLandscape ? 14 : 16, color: Colors.grey.shade600, ), textAlign: TextAlign.center, ), ], ); } Widget _buildInitialState(bool isLandscape) { return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.download_rounded, size: isLandscape ? 48 : 64, color: Colors.grey.shade400, ), SizedBox(height: isLandscape ? 12 : 20), Text( 'Siap untuk sinkronisasi', style: TextStyle( fontSize: isLandscape ? 16 : 18, fontWeight: FontWeight.w500, ), ), SizedBox(height: isLandscape ? 4 : 8), Text( 'Tekan tombol mulai untuk mengunduh data', style: TextStyle( fontSize: isLandscape ? 12 : 14, color: Colors.grey.shade600, ), textAlign: TextAlign.center, ), ], ); } Widget _buildSyncingState( SyncStep step, double progress, String message, bool isLandscape) { return SingleChildScrollView( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ // Progress circle Stack( alignment: Alignment.center, children: [ SizedBox( width: isLandscape ? 100 : 120, height: isLandscape ? 100 : 120, child: AnimatedBuilder( animation: _progressAnimation, builder: (context, child) { return CircularProgressIndicator( value: _progressAnimation.value, strokeWidth: isLandscape ? 6 : 8, backgroundColor: Colors.grey.shade200, valueColor: AlwaysStoppedAnimation(AppColors.primary), ); }, ), ), Column( children: [ Icon( _getSyncIcon(step), size: isLandscape ? 24 : 32, color: AppColors.primary, ), SizedBox(height: 2), AnimatedBuilder( animation: _progressAnimation, builder: (context, child) { return Text( '${(_progressAnimation.value * 100).toInt()}%', style: TextStyle( fontSize: isLandscape ? 14 : 16, fontWeight: FontWeight.bold, color: AppColors.primary, ), ); }, ), ], ), ], ), SizedBox(height: isLandscape ? 20 : 30), // Step indicator _buildStepIndicator(step, isLandscape), SizedBox(height: isLandscape ? 12 : 20), // Current message Container( padding: EdgeInsets.symmetric( horizontal: isLandscape ? 16 : 20, vertical: isLandscape ? 8 : 12), decoration: BoxDecoration( color: Colors.blue.shade50, borderRadius: BorderRadius.circular(8), ), child: Text( message, style: TextStyle( color: Colors.blue.shade700, fontSize: isLandscape ? 12 : 14, fontWeight: FontWeight.w500, ), textAlign: TextAlign.center, ), ), SizedBox(height: isLandscape ? 12 : 20), // Sync details _buildSyncDetails(step, progress, isLandscape), ], ), ); } Widget _buildStepIndicator(SyncStep currentStep, bool isLandscape) { final steps = [ ('Kategori', SyncStep.categories, Icons.category), ('Produk', SyncStep.products, Icons.inventory_2), ('Variant', SyncStep.variants, Icons.tune), ('Selesai', SyncStep.completed, Icons.check_circle), ]; if (isLandscape) { // Vertical layout for landscape return Column( children: steps.map((stepData) { final (label, step, icon) = stepData; final isActive = step == currentStep; final isCompleted = step.index < currentStep.index; return Container( margin: EdgeInsets.symmetric(vertical: 2), padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: isActive ? AppColors.primary.withOpacity(0.1) : isCompleted ? Colors.green.shade50 : Colors.grey.shade100, borderRadius: BorderRadius.circular(6), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon( isCompleted ? Icons.check : icon, size: 12, color: isActive ? AppColors.primary : isCompleted ? Colors.green.shade600 : Colors.grey.shade500, ), SizedBox(width: 4), Text( label, style: TextStyle( fontSize: 10, fontWeight: isActive ? FontWeight.w600 : FontWeight.normal, color: isActive ? AppColors.primary : isCompleted ? Colors.green.shade600 : Colors.grey.shade600, ), ), ], ), ); }).toList(), ); } else { // Horizontal layout for portrait return Row( mainAxisAlignment: MainAxisAlignment.center, children: steps.map((stepData) { final (label, step, icon) = stepData; final isActive = step == currentStep; final isCompleted = step.index < currentStep.index; return Container( margin: EdgeInsets.symmetric(horizontal: 4), padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: isActive ? AppColors.primary.withOpacity(0.1) : isCompleted ? Colors.green.shade50 : Colors.grey.shade100, borderRadius: BorderRadius.circular(6), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon( isCompleted ? Icons.check : icon, size: 14, color: isActive ? AppColors.primary : isCompleted ? Colors.green.shade600 : Colors.grey.shade500, ), SizedBox(width: 4), Text( label, style: TextStyle( fontSize: 11, fontWeight: isActive ? FontWeight.w600 : FontWeight.normal, color: isActive ? AppColors.primary : isCompleted ? Colors.green.shade600 : Colors.grey.shade600, ), ), ], ), ); }).toList(), ); } } Widget _buildSyncDetails(SyncStep step, double progress, bool isLandscape) { return Container( padding: EdgeInsets.all(isLandscape ? 12 : 16), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(8), border: Border.all(color: Colors.grey.shade200), ), child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Status:', style: TextStyle( fontSize: isLandscape ? 12 : 14, fontWeight: FontWeight.w500, color: Colors.grey.shade700, ), ), Text( _getStepLabel(step), style: TextStyle( fontSize: isLandscape ? 12 : 14, fontWeight: FontWeight.w600, color: AppColors.primary, ), ), ], ), SizedBox(height: isLandscape ? 6 : 8), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Progress:', style: TextStyle( fontSize: isLandscape ? 12 : 14, fontWeight: FontWeight.w500, color: Colors.grey.shade700, ), ), Text( '${(progress * 100).toInt()}%', style: TextStyle( fontSize: isLandscape ? 12 : 14, fontWeight: FontWeight.w600, color: AppColors.primary, ), ), ], ), ], ), ); } Widget _buildCompletedState(SyncStats stats, bool isLandscape) { return SingleChildScrollView( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ // Success icon Container( width: isLandscape ? 80 : 100, height: isLandscape ? 80 : 100, decoration: BoxDecoration( color: Colors.green.shade50, borderRadius: BorderRadius.circular(isLandscape ? 40 : 50), ), child: Icon( Icons.check_circle, size: isLandscape ? 48 : 60, color: Colors.green.shade600, ), ), SizedBox(height: isLandscape ? 20 : 30), Text( 'Sinkronisasi Berhasil!', style: TextStyle( fontSize: isLandscape ? 18 : 22, fontWeight: FontWeight.bold, color: Colors.green.shade700, ), ), SizedBox(height: isLandscape ? 8 : 16), Text( 'Data berhasil diunduh ke perangkat', style: TextStyle( fontSize: isLandscape ? 14 : 16, color: Colors.grey.shade600, ), ), SizedBox(height: isLandscape ? 20 : 30), // Stats cards Container( padding: EdgeInsets.all(isLandscape ? 16 : 20), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.grey.shade200), ), child: Column( children: [ Text( 'Data yang Diunduh', style: TextStyle( fontSize: isLandscape ? 14 : 16, fontWeight: FontWeight.w600, color: Colors.grey.shade700, ), ), SizedBox(height: isLandscape ? 12 : 16), if (isLandscape) // Vertical layout for landscape Column( children: [ _buildStatItem('Kategori', '${stats.totalCategories}', Icons.category, Colors.blue, isLandscape), SizedBox(height: 8), _buildStatItem('Produk', '${stats.totalProducts}', Icons.inventory_2, Colors.green, isLandscape), SizedBox(height: 8), _buildStatItem('Variant', '${stats.totalVariants}', Icons.tune, Colors.orange, isLandscape), ], ) else // Horizontal layout for portrait Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ _buildStatItem('Kategori', '${stats.totalCategories}', Icons.category, Colors.blue, isLandscape), _buildStatItem('Produk', '${stats.totalProducts}', Icons.inventory_2, Colors.green, isLandscape), _buildStatItem('Variant', '${stats.totalVariants}', Icons.tune, Colors.orange, isLandscape), ], ), ], ), ), SizedBox(height: isLandscape ? 12 : 20), Container( padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8), decoration: BoxDecoration( color: Colors.grey.shade100, borderRadius: BorderRadius.circular(6), ), child: Text( 'Mengalihkan ke halaman utama...', style: TextStyle( color: Colors.grey.shade600, fontSize: isLandscape ? 10 : 12, ), ), ), ], ), ); } Widget _buildErrorState(String message, bool isLandscape) { return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.error_outline, size: isLandscape ? 48 : 64, color: Colors.red.shade400, ), SizedBox(height: isLandscape ? 12 : 20), Text( 'Sinkronisasi Gagal', style: TextStyle( fontSize: isLandscape ? 16 : 20, fontWeight: FontWeight.bold, color: Colors.red.shade600, ), ), SizedBox(height: isLandscape ? 8 : 12), Container( padding: EdgeInsets.all(isLandscape ? 12 : 16), decoration: BoxDecoration( color: Colors.red.shade50, borderRadius: BorderRadius.circular(8), ), child: Text( message, style: TextStyle( fontSize: isLandscape ? 12 : 14, color: Colors.red.shade700, ), textAlign: TextAlign.center, ), ), SizedBox(height: isLandscape ? 12 : 20), Text( 'Periksa koneksi internet dan coba lagi', style: TextStyle( fontSize: isLandscape ? 12 : 14, color: Colors.grey.shade600, ), textAlign: TextAlign.center, ), ], ); } Widget _buildStatItem(String label, String value, IconData icon, Color color, bool isLandscape) { if (isLandscape) { // Horizontal layout for landscape return Container( padding: EdgeInsets.symmetric(horizontal: 12, vertical: 8), decoration: BoxDecoration( color: color.withOpacity(0.1), borderRadius: BorderRadius.circular(8), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(icon, size: 20, color: color), SizedBox(width: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( value, style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: color, ), ), Text( label, style: TextStyle( fontSize: 10, color: Colors.grey.shade600, ), ), ], ), ], ), ); } else { // Vertical layout for portrait return Column( children: [ Container( padding: EdgeInsets.all(12), decoration: BoxDecoration( color: color.withOpacity(0.1), borderRadius: BorderRadius.circular(8), ), child: Icon(icon, size: 24, color: color), ), SizedBox(height: 8), Text( value, style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: color, ), ), Text( label, style: TextStyle( fontSize: 12, color: Colors.grey.shade600, ), ), ], ); } } Widget _buildActions(DataSyncState state) { return state.when( initial: () => Button.filled( onPressed: () { context.read().add(const DataSyncEvent.startSync()); }, label: 'Mulai Sinkronisasi', ), syncing: (step, progress, message) => Button.outlined( onPressed: () { context.read().add(const DataSyncEvent.cancelSync()); }, label: 'Batalkan', ), completed: (stats) => Button.filled( onPressed: () { context.pushReplacement(DashboardPage()); }, label: 'Lanjutkan ke Aplikasi', ), error: (message) => Row( children: [ Expanded( child: Button.outlined( onPressed: () { context.pushReplacement(DashboardPage()); }, label: 'Lewati', ), ), SizedBox(width: 16), Expanded( child: Button.filled( onPressed: () { context .read() .add(const DataSyncEvent.startSync()); }, label: 'Coba Lagi', ), ), ], ), ); } IconData _getSyncIcon(SyncStep step) { switch (step) { case SyncStep.categories: return Icons.category; case SyncStep.products: return Icons.inventory_2; case SyncStep.variants: return Icons.tune; case SyncStep.completed: return Icons.check_circle; } } String _getStepLabel(SyncStep step) { switch (step) { case SyncStep.categories: return 'Mengunduh Kategori'; case SyncStep.products: return 'Mengunduh Produk'; case SyncStep.variants: return 'Mengunduh Variant'; case SyncStep.completed: return 'Selesai'; } } }