QueenFruits/Mobile Operasional/lib/features/home/presentation/screens/home_screen.dart

659 lines
29 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:niogu_app/core/constants/app_color.dart';
import 'package:niogu_app/core/constants/app_font_size.dart';
import 'package:niogu_app/core/providers/app_provider.dart';
import 'package:niogu_app/core/router/app_route.dart';
import 'package:niogu_app/core/services/sync_services.dart';
import 'package:niogu_app/core/utils/log_message.dart';
import 'package:niogu_app/core/utils/login_required.dart';
import 'package:niogu_app/core/enums/sync_status.dart';
import 'package:niogu_app/core/components/modal_outlet_bottom.dart';
import 'package:niogu_app/core/utils/time_zone.dart';
import 'package:niogu_app/core/enums/user_role.dart';
import 'package:niogu_app/core/widgets/custom_snackbar.dart';
import 'package:niogu_app/features/home/presentation/widgets/menu_card.dart';
import 'package:niogu_app/features/home/presentation/widgets/menu_item_card.dart';
import 'package:niogu_app/features/home/presentation/widgets/section_title.dart';
import 'package:niogu_app/features/home/presentation/widgets/sync_icon.dart';
import 'package:niogu_app/features/online_store/presentation/providers/online_store_provider.dart';
import 'package:niogu_app/features/user/domain/entities/user.dart';
import 'package:niogu_app/features/user/presentation/providers/user_provider.dart';
import 'package:sizer/sizer.dart';
class HomeScreen extends ConsumerStatefulWidget {
const HomeScreen({super.key});
@override
ConsumerState<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends ConsumerState<HomeScreen> {
SyncStatus _syncStatus = SyncStatus.synced;
bool _isProcessing = false;
final List<Map<String, dynamic>> _ownerActivityMenus = [
{'title': 'Cek Stok', 'icon': Icons.checklist, 'color': Colors.orange},
{
'title': 'Stok Masuk',
'icon': Icons.arrow_circle_down,
'color': Colors.green,
},
{'title': 'Pelanggan', 'icon': Icons.person, 'color': Colors.blue},
{'title': 'Pemasok', 'icon': Icons.car_rental, 'color': Colors.purple},
];
final List<Map<String, dynamic>> _adminActivityMenus = [
{
'id': 'stock_check',
'title': 'Cek Stok',
'icon': Icons.checklist,
'color': Colors.orange,
},
{
'id': 'stock_in',
'title': 'Stok Masuk',
'icon': Icons.arrow_circle_down,
'color': Colors.green,
},
{
'id': 'online_order',
'title': 'Pesanan Online',
'icon': Icons.shopping_bag_outlined,
'color': Colors.blue,
},
];
final List<Map<String, dynamic>> _ownerAssetMenus = [
{
"id": "online_store",
'title': 'Toko\nOnline',
'icon': Icons.language,
'color': Colors.indigo,
'firstBadge': 'Gratis',
},
{
"id": "outlets",
'title': 'Outlet\nUsaha',
'icon': Icons.store,
'color': Colors.teal,
'firstBadge': 'Scale Up',
},
];
@override
void initState() {
// TODO: implement initState
super.initState();
}
@override
void dispose() {
// TODO: implement dispose
super.dispose();
}
Future<void> _triggerFullSync() async {
if (_isProcessing) return;
setState(() {
_isProcessing = true;
_syncStatus = SyncStatus.syncing;
});
try {
final dio = ref.read(dioProvider);
final db = ref.read(appDatabaseProvider);
final syncService = SyncService(dio, db);
await syncService.restartUpSync();
await syncService.processBatch();
setState(() {
_isProcessing = false;
_syncStatus = SyncStatus.synced;
});
CustomSnackbar.showSuccess(context, "Sinkronisasi berhasil");
} catch (e, st) {
LogMessage.log.e(e.toString(), error: e, stackTrace: st);
setState(() {
_isProcessing = false;
_syncStatus = SyncStatus.failed;
});
CustomSnackbar.showError(
context,
"Pastikan teruhubung dengan koneksi internet",
);
}
}
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
final bool isTablet = 100.w >= 600;
final isLoggedIn = ref.watch(currentStatusLoginProvider);
final currentUserName = ref.watch(currentUserNameProvider);
final currentUserRole = ref.watch(currentUserRoleProvider);
final currentOutletName = ref.watch(currentOutletNameProvider);
String? badge;
if (isLoggedIn) {
final badgeCountState = ref.watch(
numberOfNeedToBeProcessedStreamProvider,
);
badgeCountState.whenData((count) {
if (count > 0) badge = count.toString();
});
}
ShiftInfo shiftInfo = ShiftInfo(
shiftName: '---',
shiftStartTime: '---',
shiftEndTime: '---',
);
if (isLoggedIn) {
final shiftInfoState = ref.watch(shiftInfoStreamProvider);
shiftInfoState.whenData((s) => shiftInfo = s);
}
return SafeArea(
top: false,
bottom: true,
right: false,
left: false,
child: Scaffold(
backgroundColor: Colors.white,
body: CustomScrollView(
slivers: [
SliverToBoxAdapter(
child: Stack(
clipBehavior: Clip.none,
children: [
Container(
height: 28.h,
padding: EdgeInsets.symmetric(
horizontal: 5.w,
vertical: 6.h,
),
decoration: BoxDecoration(
color: AppColor.primaryColor,
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(10.w),
bottomRight: Radius.circular(10.w),
),
),
child: Column(
children: [
Row(
children: [
if (isLoggedIn) ...[
CircleAvatar(
radius: 6.w,
backgroundColor: Colors.white24,
child: Icon(
Icons.person_rounded,
size: 10.w,
color: Colors.white,
), // Contoh foto user
),
SizedBox(width: 3.w),
Expanded(
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
"Halo, ${currentUserName!}",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w500,
fontSize: isTablet
? AppFontSize.medium.sp
: AppFontSize.small.sp,
),
),
if (currentUserRole ==
UserRole.admin) ...[
SizedBox(height: 0.5.h),
Text(
"${shiftInfo.shiftName} : ${shiftInfo.shiftStartTime} ${TimeZone.getCurrentTimeZone()} - ${shiftInfo.shiftEndTime} ${TimeZone.getCurrentTimeZone()}",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w500,
fontSize: isTablet
? AppFontSize.medium.sp
: AppFontSize.small.sp,
),
),
],
SizedBox(height: 0.5.h),
GestureDetector(
onTap:
currentUserRole == UserRole.admin
? null
: () {
showModalBottomSheet(
context: context,
useRootNavigator: true,
isScrollControlled: true,
constraints:
const BoxConstraints(
maxWidth:
double.infinity,
),
builder: (context) =>
const ModalOutletBottom(),
);
},
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Flexible(
child: Text(
currentOutletName!,
style: TextStyle(
color: Colors.white,
fontSize: isTablet
? AppFontSize.medium.sp
: AppFontSize.small.sp,
fontWeight: FontWeight.bold,
),
maxLines: 1,
overflow:
TextOverflow.ellipsis,
),
),
if (currentUserRole ==
UserRole.owner) ...[
SizedBox(width: 1.w),
Icon(
Icons
.keyboard_arrow_down_rounded,
color: Colors.white,
size: 5.w,
),
],
],
),
),
],
),
),
GestureDetector(
onTap: _triggerFullSync,
child: Container(
padding: EdgeInsets.all(2.w),
decoration: BoxDecoration(
color: Colors.white.withOpacity(
0.15,
), // Transparan halus
shape: BoxShape.circle,
),
child: SyncIcon(syncStatus: _syncStatus),
),
),
] else ...[
OutlinedButton.icon(
onPressed: () => context.goNamed(
AppRoute.ownerLoginScreen,
),
icon: Icon(
Icons.login_rounded,
color: Colors.white,
size: 4.5.w,
),
label: Text(
"Masuk Akun",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: isTablet
? AppFontSize.medium.sp
: AppFontSize.small.sp,
),
),
style: OutlinedButton.styleFrom(
side: const BorderSide(
color: Colors.white70,
width: 1.5,
),
padding: EdgeInsets.symmetric(
horizontal: 4.w,
vertical: 1.h,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
2.w,
),
),
backgroundColor: Colors.white.withOpacity(
0.1,
),
),
),
],
if (isTablet) SizedBox(width: 3.w),
/** Notification
Stack(
children: [
IconButton(
onPressed: () {},
padding: EdgeInsets.zero,
icon: Icon(
Icons.notifications_none_rounded,
color: Colors.white,
size: 7.w,
),
),
Positioned(
right: isTablet
? 10
: 100.w > 360
? 12.5
: 15,
top: isTablet
? 10
: 100.w > 360
? 12.5
: 15,
child: Container(
padding: EdgeInsets.all(0.5.w),
decoration: const BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
),
constraints: BoxConstraints(
minWidth: 2.5.w,
minHeight: 2.5.w,
), // Dot merah
),
),
],
),
*/
],
),
],
),
),
Positioned(
top: 16.h,
left: 0,
right: 0,
child: SizedBox(
height: 18.h,
child: PageView.builder(
controller: PageController(viewportFraction: 0.9),
itemCount: 3,
itemBuilder: (context, index) {
return Container(
margin: EdgeInsets.symmetric(horizontal: 2.w),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5.w),
gradient: LinearGradient(
colors: index == 0
? [
Colors.blueAccent,
Colors.lightBlueAccent,
]
: [
Colors.orangeAccent,
Colors.deepOrangeAccent,
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
boxShadow: [
BoxShadow(
color: AppColor.primaryColor.withOpacity(
0.2,
),
blurRadius: 10,
offset: const Offset(0, 5),
),
],
),
child: Stack(
children: [
Positioned(
right: -5.w,
bottom: -5.w,
child: Icon(
Icons.sell,
color: Colors.white.withOpacity(0.2),
size: 30.w,
),
),
Padding(
padding: EdgeInsets.all(5.w),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
mainAxisAlignment:
MainAxisAlignment.center,
children: [
Container(
padding: EdgeInsets.symmetric(
horizontal: 2.w,
vertical: 0.5.h,
),
decoration: BoxDecoration(
color: Colors.white24,
borderRadius:
BorderRadius.circular(2.w),
),
child: Text(
"Promo Baru",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w500,
fontSize: isTablet
? AppFontSize.medium.sp
: AppFontSize.small.sp,
),
),
),
SizedBox(height: 1.h),
Text(
"Diskon Spesial Hari Ini!",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: isTablet
? AppFontSize.medium.sp
: AppFontSize.small.sp,
),
),
],
),
),
],
),
);
},
),
),
),
],
),
),
SliverToBoxAdapter(child: SizedBox(height: 8.h)),
/**
SliverPadding(
padding: EdgeInsets.symmetric(horizontal: 5.w),
sliver: SliverGrid(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 1.6,
mainAxisSpacing: 2.h,
crossAxisSpacing: 3.w,
),
delegate: SliverChildBuilderDelegate((context, index) {
final menu = _ownerActivityMenus[index];
return MenuItemCard(
title: menu['title'],
icon: menu['icon'],
color: menu['color'],
badge: menu['badge'],
onTap: () {
if (index == 0) {
} else if (index == 1) {
context.pushNamed(AppRoute.stockInScreen);
}
},
);
}, childCount: _ownerActivityMenus.length),
),
),
*/
if (currentUserRole == UserRole.owner || !isLoggedIn) ...[
SectionTitle(title: "Aktivitas Utama"),
SliverPadding(
padding: EdgeInsets.symmetric(horizontal: 5.w),
sliver: SliverGrid(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4,
childAspectRatio: 0.8,
mainAxisSpacing: 2.h,
crossAxisSpacing: 2.w,
),
delegate: SliverChildBuilderDelegate((context, index) {
final menu = _ownerActivityMenus[index];
return MenuCard(
title: menu['title'],
icon: menu['icon'],
color: menu['color'],
onTap: !isLoggedIn
? () => LoginRequired.showLoginRequired(context)
: () {
if (index == 0) {
context.pushNamed(
AppRoute.stockCheckScreen,
);
} else if (index == 1) {
context.pushNamed(AppRoute.stockInScreen);
} else if (index == 2) {
context.pushNamed(AppRoute.customerScreen);
} else if (index == 3) {
context.pushNamed(AppRoute.supplierScreen);
}
},
);
}, childCount: _ownerActivityMenus.length),
),
),
SliverToBoxAdapter(child: SizedBox(height: 3.h)),
SectionTitle(title: "Aset & Manajemen"),
SliverPadding(
padding: EdgeInsets.symmetric(horizontal: 5.w),
sliver: SliverGrid(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 1.6,
mainAxisSpacing: 2.h,
crossAxisSpacing: 3.w,
),
delegate: SliverChildBuilderDelegate((context, index) {
final menu = _ownerAssetMenus[index];
return MenuItemCard(
title: menu['title'],
icon: menu['icon'],
color: menu['color'],
firstBadge: menu['firstBadge'],
secondBadge:
menu['id'] == 'online_store' && badge != null
? badge
: null,
onTap: !isLoggedIn
? () => LoginRequired.showLoginRequired(context)
: () {
if (index == 0) {
context.pushNamed(
AppRoute.onlineStoreScreen,
);
} else if (index == 1) {
context.pushNamed(AppRoute.outletScreen);
}
},
);
}, childCount: _ownerAssetMenus.length),
),
),
] else if (currentUserRole == UserRole.admin) ...[
SectionTitle(title: "Aktivitas Utama"),
SliverPadding(
padding: EdgeInsets.symmetric(horizontal: 5.w),
sliver: SliverGrid(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
childAspectRatio: 0.8,
mainAxisSpacing: 2.h,
crossAxisSpacing: 2.w,
),
delegate: SliverChildBuilderDelegate((context, index) {
final menu = _adminActivityMenus[index];
return MenuCard(
title: menu['title'],
icon: menu['icon'],
color: menu['color'],
badge: menu['id'] == 'online_order' && badge != null
? badge
: null,
onTap: () {
if (index == 0) {
context.pushNamed(AppRoute.stockCheckScreen);
} else if (index == 1) {
context.pushNamed(AppRoute.stockInScreen);
} else if (index == 2) {
context.pushNamed(
AppRoute.onlineStoreOrderScreen,
);
}
},
);
}, childCount: _adminActivityMenus.length),
),
),
],
SliverToBoxAdapter(child: SizedBox(height: 12.h)),
],
),
),
);
},
);
}
}