diff --git a/lib/presentation/data_sync/pages/data_sync_page.dart b/lib/presentation/data_sync/pages/data_sync_page.dart index fa397e3..27ae572 100644 --- a/lib/presentation/data_sync/pages/data_sync_page.dart +++ b/lib/presentation/data_sync/pages/data_sync_page.dart @@ -1,15 +1,9 @@ -// ======================================== -// DATA SYNC PAGE - POST LOGIN SYNC -// lib/presentation/sync/pages/data_sync_page.dart -// ======================================== - 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/components/spaces.dart'; import '../../../core/constants/colors.dart'; import '../bloc/data_sync_bloc.dart'; @@ -54,6 +48,11 @@ class _DataSyncPageState extends State @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( @@ -77,71 +76,121 @@ class _DataSyncPageState extends State ); }, builder: (context, state) { - return Padding( - padding: EdgeInsets.all(24), - child: Column( - children: [ - SpaceHeight(60), - - // Header - _buildHeader(), - - SpaceHeight(60), - - // Sync progress - Expanded( - child: state.when( - initial: () => _buildInitialState(), - syncing: (step, progress, message) => - _buildSyncingState(step, progress, message), - completed: (stats) => _buildCompletedState(stats), - error: (message) => _buildErrorState(message), - ), - ), - - SpaceHeight(40), - - // Actions - _buildActions(state), - ], - ), - ); + if (isLandscape) { + return _buildLandscapeLayout(state, screenWidth, screenHeight); + } else { + return _buildPortraitLayout(state, screenHeight); + } }, ), ), ); } - Widget _buildHeader() { + // Portrait layout (original) + Widget _buildPortraitLayout(DataSyncState state, double screenHeight) { + return Padding( + padding: EdgeInsets.all(24), + child: Column( + children: [ + SizedBox(height: screenHeight * 0.08), // Responsive spacing + + // Header + _buildHeader(false), + + SizedBox(height: screenHeight * 0.08), + + // Sync progress + 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), + + // Actions + _buildActions(state), + ], + ), + ); + } + + // Landscape layout (side by side) + Widget _buildLandscapeLayout( + DataSyncState state, double screenWidth, double screenHeight) { + return Padding( + padding: EdgeInsets.symmetric(horizontal: 32, vertical: 16), + child: Row( + children: [ + // Left side - Header and info + Expanded( + flex: 2, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + _buildHeader(true), + SizedBox(height: 20), + _buildActions(state), + ], + ), + ), + + SizedBox(width: 40), + + // Right side - Sync progress + 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: 80, - height: 80, + width: isLandscape ? 60 : 80, + height: isLandscape ? 60 : 80, decoration: BoxDecoration( color: AppColors.primary.withOpacity(0.1), - borderRadius: BorderRadius.circular(20), + borderRadius: BorderRadius.circular(isLandscape ? 15 : 20), ), child: Icon( Icons.sync, - size: 40, + size: isLandscape ? 30 : 40, color: AppColors.primary, ), ), - SpaceHeight(20), + SizedBox(height: isLandscape ? 12 : 20), Text( 'Sinkronisasi Data', style: TextStyle( - fontSize: 24, + fontSize: isLandscape ? 20 : 24, fontWeight: FontWeight.bold, color: Colors.grey.shade800, ), ), - SpaceHeight(8), + SizedBox(height: isLandscape ? 4 : 8), Text( 'Mengunduh data terbaru ke perangkat', style: TextStyle( - fontSize: 16, + fontSize: isLandscape ? 14 : 16, color: Colors.grey.shade600, ), textAlign: TextAlign.center, @@ -150,117 +199,125 @@ class _DataSyncPageState extends State ); } - Widget _buildInitialState() { + Widget _buildInitialState(bool isLandscape) { return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.download_rounded, - size: 64, + size: isLandscape ? 48 : 64, color: Colors.grey.shade400, ), - SpaceHeight(20), + SizedBox(height: isLandscape ? 12 : 20), Text( 'Siap untuk sinkronisasi', style: TextStyle( - fontSize: 18, + fontSize: isLandscape ? 16 : 18, fontWeight: FontWeight.w500, ), ), - SpaceHeight(8), + 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) { - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - // Progress circle - Stack( - alignment: Alignment.center, - children: [ - SizedBox( - width: 120, - height: 120, - child: AnimatedBuilder( - animation: _progressAnimation, - builder: (context, child) { - return CircularProgressIndicator( - value: _progressAnimation.value, - strokeWidth: 8, - backgroundColor: Colors.grey.shade200, - valueColor: - AlwaysStoppedAnimation(AppColors.primary), - ); - }, - ), - ), - Column( - children: [ - Icon( - _getSyncIcon(step), - size: 32, - color: AppColors.primary, - ), - SpaceHeight(4), - AnimatedBuilder( + 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 Text( - '${(_progressAnimation.value * 100).toInt()}%', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - color: AppColors.primary, - ), + return CircularProgressIndicator( + value: _progressAnimation.value, + strokeWidth: isLandscape ? 6 : 8, + backgroundColor: Colors.grey.shade200, + valueColor: + AlwaysStoppedAnimation(AppColors.primary), ); }, ), - ], - ), - ], - ), - - SpaceHeight(30), - - // Step indicator - _buildStepIndicator(step), - - SpaceHeight(20), - - // Current message - Container( - padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), - decoration: BoxDecoration( - color: Colors.blue.shade50, - borderRadius: BorderRadius.circular(8), + ), + 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, + ), + ); + }, + ), + ], + ), + ], ), - child: Text( - message, - style: TextStyle( - color: Colors.blue.shade700, - fontWeight: FontWeight.w500, + + 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, ), - textAlign: TextAlign.center, ), - ), - SpaceHeight(20), + SizedBox(height: isLandscape ? 12 : 20), - // Sync details - _buildSyncDetails(step, progress), - ], + // Sync details + _buildSyncDetails(step, progress, isLandscape), + ], + ), ); } - Widget _buildStepIndicator(SyncStep currentStep) { + Widget _buildStepIndicator(SyncStep currentStep, bool isLandscape) { final steps = [ ('Produk', SyncStep.products, Icons.inventory_2), ('Kategori', SyncStep.categories, Icons.category), @@ -268,59 +325,111 @@ class _DataSyncPageState extends State ('Selesai', SyncStep.completed, Icons.check_circle), ]; - return Row( - mainAxisAlignment: MainAxisAlignment.center, - children: steps.map((stepData) { - final (label, step, icon) = stepData; - final isActive = step == currentStep; - final isCompleted = step.index < currentStep.index; + 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(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, + 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.shade600, + : Colors.grey.shade500, ), - ), - ], - ), - ); - }).toList(), - ); + 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) { + Widget _buildSyncDetails(SyncStep step, double progress, bool isLandscape) { return Container( - padding: EdgeInsets.all(16), + padding: EdgeInsets.all(isLandscape ? 12 : 16), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(8), @@ -334,6 +443,7 @@ class _DataSyncPageState extends State Text( 'Status:', style: TextStyle( + fontSize: isLandscape ? 12 : 14, fontWeight: FontWeight.w500, color: Colors.grey.shade700, ), @@ -341,19 +451,21 @@ class _DataSyncPageState extends State Text( _getStepLabel(step), style: TextStyle( + fontSize: isLandscape ? 12 : 14, fontWeight: FontWeight.w600, color: AppColors.primary, ), ), ], ), - SpaceHeight(8), + 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, ), @@ -361,6 +473,7 @@ class _DataSyncPageState extends State Text( '${(progress * 100).toInt()}%', style: TextStyle( + fontSize: isLandscape ? 12 : 14, fontWeight: FontWeight.w600, color: AppColors.primary, ), @@ -372,135 +485,141 @@ class _DataSyncPageState extends State ); } - Widget _buildCompletedState(SyncStats stats) { - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - // Success icon - Container( - width: 100, - height: 100, - decoration: BoxDecoration( - color: Colors.green.shade50, - borderRadius: BorderRadius.circular(50), - ), - child: Icon( - Icons.check_circle, - size: 60, - color: Colors.green.shade600, - ), - ), - - SpaceHeight(30), - - Text( - 'Sinkronisasi Berhasil!', - style: TextStyle( - fontSize: 22, - fontWeight: FontWeight.bold, - color: Colors.green.shade700, - ), - ), - - SpaceHeight(16), - - Text( - 'Data berhasil diunduh ke perangkat', - style: TextStyle( - fontSize: 16, - color: Colors.grey.shade600, - ), - ), - - SpaceHeight(30), - - // Stats cards - Container( - padding: EdgeInsets.all(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: 16, - fontWeight: FontWeight.w600, - color: Colors.grey.shade700, - ), - ), - SpaceHeight(16), - Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - _buildStatItem( - 'Produk', - '${stats.totalProducts}', - Icons.inventory_2, - Colors.blue, - ), - _buildStatItem( - 'Kategori', - '${stats.totalCategories}', - Icons.category, - Colors.green, - ), - _buildStatItem( - 'Variant', - '${stats.totalVariants}', - Icons.tune, - Colors.orange, - ), - ], - ), - ], - ), - ), - - SpaceHeight(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: 12, + 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('Produk', '${stats.totalProducts}', + Icons.inventory_2, Colors.blue, isLandscape), + SizedBox(height: 8), + _buildStatItem('Kategori', '${stats.totalCategories}', + Icons.category, 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('Produk', '${stats.totalProducts}', + Icons.inventory_2, Colors.blue, isLandscape), + _buildStatItem('Kategori', '${stats.totalCategories}', + Icons.category, 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) { + Widget _buildErrorState(String message, bool isLandscape) { return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.error_outline, - size: 64, + size: isLandscape ? 48 : 64, color: Colors.red.shade400, ), - SpaceHeight(20), + SizedBox(height: isLandscape ? 12 : 20), Text( 'Sinkronisasi Gagal', style: TextStyle( - fontSize: 20, + fontSize: isLandscape ? 16 : 20, fontWeight: FontWeight.bold, color: Colors.red.shade600, ), ), - SpaceHeight(12), + SizedBox(height: isLandscape ? 8 : 12), Container( - padding: EdgeInsets.all(16), + padding: EdgeInsets.all(isLandscape ? 12 : 16), decoration: BoxDecoration( color: Colors.red.shade50, borderRadius: BorderRadius.circular(8), @@ -508,15 +627,17 @@ class _DataSyncPageState extends State child: Text( message, style: TextStyle( + fontSize: isLandscape ? 12 : 14, color: Colors.red.shade700, ), textAlign: TextAlign.center, ), ), - SpaceHeight(20), + 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, @@ -525,40 +646,75 @@ class _DataSyncPageState extends State ); } - Widget _buildStatItem( - String label, String value, IconData icon, Color color) { - 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, - ), + 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), ), - SpaceHeight(8), - Text( - value, - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - color: color, - ), + 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, + ), + ), + ], + ), + ], ), - Text( - label, - style: TextStyle( - fontSize: 12, - 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) { @@ -577,7 +733,7 @@ class _DataSyncPageState extends State ), completed: (stats) => Button.filled( onPressed: () { - Navigator.of(context).pushReplacementNamed('/home'); + context.pushReplacement(DashboardPage()); }, label: 'Lanjutkan ke Aplikasi', ), @@ -586,7 +742,7 @@ class _DataSyncPageState extends State Expanded( child: Button.outlined( onPressed: () { - Navigator.of(context).pushReplacementNamed('/home'); + context.pushReplacement(DashboardPage()); }, label: 'Lewati', ),