import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_contacts/flutter_contacts.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:niogu_app/core/widgets/custom_error_screen.dart'; import 'package:niogu_app/core/providers/app_provider.dart'; import 'package:niogu_app/core/router/app_route.dart'; import 'package:niogu_app/core/enums/user_role.dart'; import 'package:niogu_app/core/widgets/custom_empty_screen.dart'; import 'package:niogu_app/core/widgets/custom_snackbar.dart'; import 'package:niogu_app/core/widgets/pop_up_notification.dart'; import 'package:niogu_app/features/pos/domain/entities/pos.dart'; import 'package:niogu_app/features/pos/presentation/providers/pos_provider.dart'; import 'package:niogu_app/features/pos/presentation/widgets/already_customer_shimmer.dart'; import 'package:niogu_app/features/pos/presentation/widgets/checkbox_customer.dart'; import 'package:sizer/sizer.dart'; import 'package:niogu_app/core/constants/app_color.dart'; import 'package:niogu_app/core/constants/app_font_size.dart'; class AlreadyCustomerScreen extends ConsumerStatefulWidget { const AlreadyCustomerScreen({super.key}); @override ConsumerState createState() => _AlreadyCustomerScreenState(); } class _AlreadyCustomerScreenState extends ConsumerState { final FocusNode _searchFocusNode = FocusNode(); Color _searchIconColor = Colors.grey; Timer? _debounce; String? _selectedCustomerId; String? _selectedCustomerAddress; SelectedCustomer? _selectedCustomer; @override void initState() { // TODO: implement initState super.initState(); _searchFocusNode.addListener(() { setState(() { _searchIconColor = _searchFocusNode.hasFocus ? Colors.black : Colors.grey; }); }); } @override void dispose() { // TODO: implement dispose _searchFocusNode.dispose(); _debounce?.cancel(); super.dispose(); } void _onSearchChanged(String value) { if (_debounce?.isActive ?? false) _debounce?.cancel(); _debounce = Timer(const Duration(milliseconds: 800), () { ref.read(alreadyCustomerSearchProvider.notifier).state = value; }); } Future _handleImportContact() async { final permission = await FlutterContacts.requestPermission(); if (permission) { final contact = await FlutterContacts.openExternalPick(); if (contact != null) { String name = contact.displayName; String phone = ""; if (contact.phones.isNotEmpty) { phone = contact.phones.first.number; } if (!mounted) return; context.pushNamed( AppRoute.addCustomerScreen, extra: {'name': name, 'phone': phone}, ); } } else { CustomSnackbar.showWarning(context, "Akses Ditolak"); } } void _showAddOptions(BuildContext context) { final bool isTablet = 100.w >= 600; showModalBottomSheet( context: context, backgroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(2.5.w)), ), constraints: const BoxConstraints(maxWidth: double.infinity), builder: (context) { return SafeArea( child: Container( width: 100.w, padding: EdgeInsets.symmetric(vertical: 2.h), clipBehavior: Clip.hardEdge, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.vertical(top: Radius.circular(6.w)), ), child: Column( mainAxisSize: MainAxisSize.min, children: [ Padding( padding: EdgeInsets.fromLTRB(5.w, 2.h, 5.w, 1.h), child: Text( "Tambah Pelanggan", style: TextStyle( fontSize: AppFontSize.medium.sp, fontWeight: FontWeight.bold, ), ), ), SizedBox(height: 2.h), ListTile( leading: Padding( padding: EdgeInsets.only(left: 5.w), child: Icon( Icons.edit_note_rounded, color: Colors.blue, size: 6.w, ), ), title: Text( "Input Manual", style: TextStyle( fontWeight: FontWeight.bold, fontSize: isTablet ? AppFontSize.medium.sp : AppFontSize.small.sp, ), ), subtitle: Text( "Isi informasi pelanggan secara manual", style: TextStyle( fontSize: isTablet ? (AppFontSize.medium - 1.25).sp : (AppFontSize.small - 1.25).sp, color: Colors.grey, ), ), onTap: () { context.pop(); context.pushNamed(AppRoute.addCustomerScreen); }, ), SizedBox(height: 2.h), ListTile( leading: Padding( padding: EdgeInsets.only(left: 5.w), child: Icon( Icons.contacts_rounded, color: Colors.green, size: 6.w, ), ), title: Text( "Ambil dari Kontak HP", style: TextStyle( fontWeight: FontWeight.bold, fontSize: isTablet ? AppFontSize.medium.sp : AppFontSize.small.sp, ), ), subtitle: Text( "Pilih langsung dari buku telepon", style: TextStyle( fontSize: isTablet ? (AppFontSize.medium - 1.25).sp : (AppFontSize.small - 1.25).sp, color: Colors.grey, ), ), onTap: () async { context.pop(); await _handleImportContact(); }, ), SizedBox(height: 2.h), ], ), ), ); }, ); } void _selectCustomer( SelectedCustomer selectedCustomer, String customerId, String address, ) { setState(() { if (_selectedCustomerId == customerId && _selectedCustomerAddress == address) { _selectedCustomerId = null; _selectedCustomerAddress = null; _selectedCustomer = null; } else { _selectedCustomerId = customerId; _selectedCustomerAddress = address; _selectedCustomer = selectedCustomer; } }); } void _submitSelection() { ref.read(selectedCustomerProvider.notifier).state = _selectedCustomer; context.pop(); } @override Widget build(BuildContext context) { return LayoutBuilder( builder: (context, constraints) { final bool isTablet = 100.w >= 600; const greyColor = Color(0xFFF5F5F5); final messageActivity = ref.watch(messageActivityProvider); final currentUserRole = ref.watch(currentUserRoleProvider); final filteredCustomerState = ref.watch( filteredAlreadyCustomerProvider, ); final customerEmptyState = ref.watch(alreadyCustomerEmptyProvider); return SafeArea( top: false, bottom: true, right: false, left: false, child: Stack( children: [ Scaffold( backgroundColor: Colors.grey[50], appBar: AppBar( backgroundColor: Colors.white, surfaceTintColor: Colors.white, elevation: 0, toolbarHeight: 10.h, titleSpacing: 0, automaticallyImplyLeading: false, title: Padding( padding: EdgeInsets.symmetric(horizontal: 4.w), child: Row( children: [ Center( child: Material( color: Colors.transparent, type: MaterialType.canvas, child: InkWell( onTap: () => context.pop(), borderRadius: BorderRadius.circular(2.5.w), child: Container( width: 10.w, height: 10.w, decoration: BoxDecoration( color: Colors.white, border: Border.all( color: Colors.grey.shade200, ), borderRadius: BorderRadius.circular(2.5.w), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.05), blurRadius: 5, offset: const Offset(0, 2), ), ], ), child: Icon( Icons.arrow_back_ios_new_rounded, color: Colors.black87, size: 5.w, ), ), ), ), ), SizedBox(width: 3.w), Expanded( child: Container( height: 6.h, padding: isTablet ? EdgeInsets.symmetric( vertical: 1.h, horizontal: 2.w, ) : EdgeInsets.zero, decoration: BoxDecoration( color: greyColor, borderRadius: BorderRadius.circular(2.5.w), ), child: TextField( focusNode: _searchFocusNode, onChanged: (value) => _onSearchChanged(value), textAlignVertical: TextAlignVertical.center, style: TextStyle( color: Colors.black87, fontSize: isTablet ? AppFontSize.medium.sp : AppFontSize.small.sp, ), decoration: InputDecoration( hintText: "Cari nama pelanggan...", hintStyle: TextStyle( color: _searchIconColor, fontSize: isTablet ? AppFontSize.medium.sp : AppFontSize.small.sp, ), prefixIcon: Icon( Icons.search, color: _searchIconColor, size: 5.w, ), border: InputBorder.none, contentPadding: EdgeInsets.zero, isDense: true, ), ), ), ), SizedBox(width: 2.w), IconButton( onPressed: () => _showAddOptions(context), icon: Icon( Icons.add_circle, color: AppColor.primaryColor, size: 8.w, ), ), ], ), ), bottom: PreferredSize( preferredSize: Size.fromHeight(2), child: Container(color: Colors.grey.shade100, height: 1), ), ), body: filteredCustomerState.when( data: (customers) { switch (customerEmptyState) { case AlreadyCustomerEmpty.loading: return SizedBox(); case AlreadyCustomerEmpty.empty_database: return CustomEmptyScreen( title: "Tidak Ada Pelanggan", body: "Kamu belum memiliki pelanggan", ); case AlreadyCustomerEmpty.empty_search_result: return CustomEmptyScreen( body: "Pelanggan Tidak Ditemukan", ); case AlreadyCustomerEmpty.has_data: return ListView.builder( padding: EdgeInsets.symmetric( vertical: 2.h, horizontal: 5.w, ), itemCount: customers.length, itemBuilder: (context, index) { final customer = customers[index]; final bool hasMultiAddress = customer.addresses.length > 1; final bool isSingleSelected = !hasMultiAddress && _selectedCustomerId == customer.id; final bool isExpanded = ref.watch( expandedAlreadyCustomerProvider(customer.id), ); return AnimatedContainer( duration: const Duration(milliseconds: 200), margin: EdgeInsets.only(bottom: 2.h), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(4.w), border: Border.all( color: Colors.grey.shade200, width: 1, ), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.03), blurRadius: 8, offset: const Offset(0, 4), ), ], ), child: Column( children: [ InkWell( onTap: () { if (hasMultiAddress) { ref .read( expandedAlreadyCustomerProvider( customer.id, ).notifier, ) .state = !ref.read( expandedAlreadyCustomerProvider( customer.id, ), ); } else { _selectCustomer( SelectedCustomer( id: customer.id, outletId: ref.read( currentOutletIdProvider, )!, name: customer.name, phoneNumber: customer.phoneNumber, address: customer.addresses.first, ), customer.id, customer.addresses.first, ); } }, borderRadius: BorderRadius.vertical( top: Radius.circular(4.w), bottom: (hasMultiAddress && isExpanded) ? Radius.zero : Radius.circular(4.w), ), child: Padding( padding: EdgeInsets.all(3.w), child: Row( children: [ Container( width: 12.w, height: 12.w, decoration: BoxDecoration( color: Colors.grey[100], shape: BoxShape.circle, ), alignment: Alignment.center, child: Text( customer.name[0].toUpperCase(), style: TextStyle( fontWeight: FontWeight.bold, fontSize: isTablet ? AppFontSize.medium.sp : AppFontSize.small.sp, color: Colors.grey, ), ), ), SizedBox(width: 4.w), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( customer.name, style: TextStyle( fontSize: isTablet ? AppFontSize.medium.sp : AppFontSize.small.sp, fontWeight: FontWeight.bold, color: Colors.black87, ), ), if (customer .phoneNumber .isNotEmpty) ...[ SizedBox(height: 0.75.h), Row( children: [ Icon( Icons.phone_android, size: 5.w, color: Colors.grey, ), SizedBox(width: 1.w), Text( customer.phoneNumber, style: TextStyle( fontSize: isTablet ? AppFontSize .medium .sp : AppFontSize .small .sp, color: Colors.grey[600], ), ), ], ), ], if (customer .addresses .first .isNotEmpty && !hasMultiAddress) ...[ SizedBox(height: 0.75.h), Row( children: [ Icon( Icons .location_on_outlined, color: Colors.grey, size: 5.w, ), SizedBox(width: 1.w), Expanded( child: Text( customer .addresses .first, overflow: TextOverflow .ellipsis, style: TextStyle( fontSize: isTablet ? AppFontSize .medium .sp : AppFontSize .small .sp, color: Colors .grey[500], ), ), ), ], ), ], ], ), ), if (customer .addresses .isNotEmpty) ...[ if (hasMultiAddress) Icon( isExpanded ? Icons .keyboard_arrow_up_rounded : Icons .keyboard_arrow_down_rounded, color: Colors.grey, size: 7.w, ) else CheckboxCustomer( isSelected: isSingleSelected, ), ], ], ), ), ), if (hasMultiAddress && isExpanded) ...[ Container( height: 1, color: Colors.grey[100], ), Padding( padding: EdgeInsets.symmetric( vertical: 1.h, ), child: Column( children: customer.addresses.map(( address, ) { final bool isAddressSelected = _selectedCustomerId == customer.id && _selectedCustomerAddress == address; return InkWell( onTap: () { _selectCustomer( SelectedCustomer( id: customer.id, outletId: ref.read( currentOutletIdProvider, )!, name: customer.name, phoneNumber: customer.phoneNumber, address: address, ), customer.id, address, ); }, child: Padding( padding: EdgeInsets.symmetric( horizontal: 4.w, vertical: 1.5.h, ), child: Row( children: [ SizedBox(width: 4.w), CheckboxCustomer( isSelected: isAddressSelected, ), SizedBox(width: 3.w), Expanded( child: Text( address, overflow: TextOverflow.ellipsis, style: TextStyle( fontSize: isTablet ? AppFontSize .medium .sp : AppFontSize .small .sp, color: isAddressSelected ? Colors.black : Colors.grey[700], fontWeight: isAddressSelected ? FontWeight.bold : FontWeight.normal, ), ), ), ], ), ), ); }).toList(), ), ), ], ], ), ); }, ); } }, error: (error, stackTrace) { return CustomErrorScreen( message: "Ups, terjadi kesalahan", onRefresh: () {}, ); }, loading: () => const AlreadyCustomerShimmer(), ), bottomNavigationBar: Container( padding: EdgeInsets.all(5.w), decoration: BoxDecoration( color: Colors.white, boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.05), blurRadius: 10, offset: const Offset(0, -5), ), ], ), child: SizedBox( height: 6.5.h, child: ElevatedButton( onPressed: (_selectedCustomerId != null) ? _submitSelection : null, style: ElevatedButton.styleFrom( backgroundColor: AppColor.primaryColor, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(2.5.w), ), elevation: 0, disabledBackgroundColor: Colors.grey.shade300, ), child: Text( "Pilih Pelanggan", style: TextStyle( color: Colors.white, fontSize: AppFontSize.medium.sp, fontWeight: FontWeight.bold, ), ), ), ), ), ), if (messageActivity != null) PopupNotification( isOwner: currentUserRole == UserRole.owner, messages: messageActivity.messages, type: messageActivity.type, ), ], ), ); }, ); } }