import 'package:flutter/material.dart'; import 'package:flutter_feather_icons/flutter_feather_icons.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:mobile_pos/Const/api_config.dart'; import 'package:mobile_pos/GlobalComponents/url_lanuncer.dart'; import 'package:mobile_pos/Provider/transactions_provider.dart'; import 'package:mobile_pos/Screens/Customers/edit_customer.dart'; import 'package:mobile_pos/Screens/Customers/sms_sent_confirmation.dart'; import 'package:mobile_pos/constant.dart'; import 'package:mobile_pos/core/theme/_app_colors.dart'; import 'package:mobile_pos/generated/l10n.dart' as lang; import 'package:mobile_pos/widgets/empty_widget/_empty_widget.dart'; import 'package:nb_utils/nb_utils.dart'; import 'package:url_launcher/url_launcher.dart'; import '../../GlobalComponents/glonal_popup.dart'; import '../../GlobalComponents/sales_transaction_widget.dart'; import '../../PDF Invoice/purchase_invoice_pdf.dart'; import '../../Provider/profile_provider.dart'; import '../../currency.dart'; import '../../http_client/custome_http_client.dart'; import '../../service/check_actions_when_no_branch.dart'; import '../../thermal priting invoices/model/print_transaction_model.dart'; import '../../thermal priting invoices/provider/print_thermal_invoice_provider.dart'; import '../../service/check_user_role_permission_provider.dart'; import '../invoice_details/purchase_invoice_details.dart'; import 'Model/parties_model.dart'; import 'Repo/parties_repo.dart'; import 'add_customer.dart'; // ignore: must_be_immutable class CustomerDetails extends ConsumerStatefulWidget { CustomerDetails({super.key, required this.party}); Party party; @override ConsumerState createState() => _CustomerDetailsState(); } class _CustomerDetailsState extends ConsumerState { @override void initState() { super.initState(); } Future showDeleteConfirmationAlert({ required BuildContext context, required String id, required WidgetRef ref, }) async { return showDialog( context: context, builder: (BuildContext context1) { return AlertDialog( title: Text( lang.S.of(context).confirmPassword, //'Confirm Delete' ), content: Text( lang.S.of(context).areYouSureYouWant, //'Are you sure you want to delete this party?' ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: Text( lang.S.of(context).cancel, //'Cancel' ), ), TextButton( onPressed: () async { Navigator.pop(context); final party = PartyRepository(); await party.deleteParty(id: id, context: context, ref: ref); }, child: Text(lang.S.of(context).delete, // 'Delete', style: const TextStyle(color: Colors.red)), ), ], ); }, ); } int selectedIndex = 0; @override Widget build(BuildContext context) { return Consumer(builder: (context, cRef, __) { final providerData = cRef.watch(salesTransactionProvider); final purchaseList = cRef.watch(purchaseTransactionProvider); final printerData = cRef.watch(thermalPrinterProvider); final businessInfo = cRef.watch(businessInfoProvider); final permissionService = PermissionService(cRef); final _theme = Theme.of(context); return GlobalPopup( child: Scaffold( backgroundColor: kWhite, appBar: AppBar( surfaceTintColor: kWhite, backgroundColor: Colors.white, title: Text( widget.party.type != 'Supplier' ? lang.S.of(context).CustomerDetails : lang.S.of(context).supplierDetails, ), actions: [ businessInfo.when(data: (details) { return Row( children: [ IconButton( visualDensity: const VisualDensity(horizontal: -4, vertical: -4), padding: EdgeInsets.zero, onPressed: () async { bool result = await checkActionWhenNoBranch(ref: ref, context: context); if (!permissionService.hasPermission(Permit.partiesUpdate.value)) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( backgroundColor: Colors.red, content: Text(lang.S.of(context).updatePartyWarn), ), ); return; } if (result) { AddParty(customerModel: widget.party).launch(context); } }, icon: const Icon( FeatherIcons.edit2, color: Colors.grey, size: 20, ), ), Padding( padding: const EdgeInsets.only(right: 8), child: IconButton( visualDensity: const VisualDensity(horizontal: -4, vertical: -4), padding: EdgeInsets.zero, onPressed: () async { bool result = await checkActionWhenNoBranch(ref: ref, context: context); if (!permissionService.hasPermission(Permit.partiesDelete.value)) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( backgroundColor: Colors.red, content: Text(lang.S.of(context).deletePartyWarn), ), ); return; } if (result) { await showDeleteConfirmationAlert( context: context, id: widget.party.id.toString(), ref: cRef); } }, icon: const Icon( FeatherIcons.trash2, color: Colors.grey, size: 20, ), ), ), ], ); }, error: (e, stack) { return Text(e.toString()); }, loading: () { return const Center( child: CircularProgressIndicator(), ); }) ], centerTitle: true, iconTheme: const IconThemeData(color: Colors.black), elevation: 0.0, ), body: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ if (permissionService.hasPermission(Permit.partiesRead.value)) ...{ Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 30), widget.party.image == null ? Center( child: Container( height: 100, width: 100, decoration: BoxDecoration( shape: BoxShape.circle, color: _theme.colorScheme.primary, ), child: Center( child: Text( (widget.party.name != null && widget.party.name!.length >= 2) ? widget.party.name!.substring(0, 2) : (widget.party.name != null ? widget.party.name! : ''), style: _theme.textTheme.bodyMedium?.copyWith( color: Colors.white, fontSize: 21, fontWeight: FontWeight.w700, ), ), ), ), ) : Center( child: Container( height: 100, width: 100, decoration: BoxDecoration( shape: BoxShape.circle, image: widget.party.image == null ? const DecorationImage( image: AssetImage('images/no_shop_image.png'), fit: BoxFit.cover, ) : DecorationImage( image: NetworkImage('${APIConfig.domain}${widget.party.image!}'), fit: BoxFit.cover, ), ), ), ), SizedBox(height: 16), Text( // 'Personal Info:', lang.S.of(context).personalInfo, style: _theme.textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w600), ), SizedBox(height: 4), ...{ lang.S.of(context).name: widget.party.name, lang.S.of(context).type: widget.party.type, lang.S.of(context).phoneNumber: widget.party.phone, lang.S.of(context).email: widget.party.email ?? "n/a", lang.S.of(context).dueBalance: "$currency${(widget.party.due ?? "0")}", lang.S.of(context).walletBalance: "$currency${(widget.party.wallet ?? "0")}", lang.S.of(context).address: widget.party.address ?? "n/a", // "Party Credit Limit": widget.party.creditLimit ?? "0", // "Party GST": widget.party.creditLimit ?? "0", }.entries.map((entry) { return keyValueWidget(title: entry.key, value: entry.value.toString(), context: context); }), SizedBox(height: 19), Text( // 'Billing Address:', lang.S.of(context).billingAddress, style: _theme.textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, ), ), SizedBox(height: 4), Text( () { final parts = [ widget.party.billingAddress?.address, widget.party.billingAddress?.city, widget.party.billingAddress?.state, widget.party.billingAddress?.zipCode, widget.party.billingAddress?.country, ].where((part) => part != null && part.isNotEmpty).toList(); return parts.isEmpty ? 'n/a' : parts.join(', '); }(), style: _theme.textTheme.bodyMedium?.copyWith( color: kPeraColor, ), ), SizedBox(height: 12), Text( // 'Shipping Address:', lang.S.of(context).shippingAddress, style: _theme.textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, ), ), SizedBox(height: 8), Text( () { final parts = [ widget.party.shippingAddress?.address, widget.party.shippingAddress?.city, widget.party.shippingAddress?.state, widget.party.shippingAddress?.zipCode, widget.party.shippingAddress?.country, ].where((part) => part != null && part.isNotEmpty).toList(); return parts.isEmpty ? 'n/a' : parts.join(', '); }(), style: _theme.textTheme.bodyMedium?.copyWith( color: kPeraColor, ), ), SizedBox(height: 12), Divider( height: 1, thickness: 1, color: DAppColors.kDividerColor, ), SizedBox(height: 12), Text( lang.S.of(context).recentTransaction, style: _theme.textTheme.titleMedium?.copyWith( fontWeight: FontWeight.w600, ), ), ], ), ), // const SizedBox(height: 8), widget.party.type != 'Supplier' ? providerData.when(data: (transaction) { final filteredTransactions = transaction.where((t) => t.party?.id == widget.party.id).toList(); return filteredTransactions.isNotEmpty ? ListView.builder( padding: EdgeInsets.zero, shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemCount: filteredTransactions.length, itemBuilder: (context, index) { final currentTransaction = filteredTransactions[index]; return salesTransactionWidget( context: context, ref: cRef, businessInfo: businessInfo.value!, sale: currentTransaction, advancePermission: true, showProductQTY: true, ); }, ) : EmptyWidget( message: TextSpan(text: lang.S.of(context).noTransaction), ); }, error: (e, stack) { return Text(e.toString()); }, loading: () { return const Center(child: CircularProgressIndicator()); }) : Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), child: purchaseList.when(data: (pTransaction) { final filteredTransactions = pTransaction.where((t) => t.party?.id == widget.party.id).toList(); return filteredTransactions.isNotEmpty ? ListView.builder( padding: EdgeInsets.zero, shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemCount: filteredTransactions.length, itemBuilder: (context, index) { final currentTransaction = filteredTransactions[index]; return GestureDetector( onTap: () { PurchaseInvoiceDetails( transitionModel: currentTransaction, businessInfo: businessInfo.value!, ).launch(context); }, child: Column( children: [ SizedBox( width: context.width(), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( "${lang.S.of(context).totalProduct} : ${currentTransaction.details!.length.toString()}", style: const TextStyle(fontSize: 16), ), Text('#${currentTransaction.invoiceNumber}'), ], ), const SizedBox(height: 2), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2), decoration: BoxDecoration( color: currentTransaction.dueAmount! <= 0 ? const Color(0xff0dbf7d).withValues(alpha: 0.1) : const Color(0xFFED1A3B).withValues(alpha: 0.1), borderRadius: const BorderRadius.all(Radius.circular(2)), ), child: Text( currentTransaction.dueAmount! <= 0 ? lang.S.of(context).paid : lang.S.of(context).unPaid, style: TextStyle( color: currentTransaction.dueAmount! <= 0 ? const Color(0xff0dbf7d) : const Color(0xFFED1A3B), ), ), ), Text(currentTransaction.purchaseDate!.substring(0, 10), style: _theme.textTheme.bodyMedium ?.copyWith(color: DAppColors.kSecondary)), ], ), const SizedBox(height: 10), Text( '${lang.S.of(context).total} : $currency${currentTransaction.totalAmount.toString()}', style: _theme.textTheme.bodyMedium ?.copyWith(color: DAppColors.kSecondary), ), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( '${lang.S.of(context).due}: $currency${currentTransaction.dueAmount.toString()}', style: const TextStyle(fontSize: 16), ), businessInfo.when(data: (data) { return Row( children: [ IconButton( onPressed: () async { PrintPurchaseTransactionModel model = PrintPurchaseTransactionModel( purchaseTransitionModel: currentTransaction, personalInformationModel: data, ); await printerData.printPurchaseThermalInvoiceNow( transaction: model, productList: model.purchaseTransitionModel!.details, invoiceSize: businessInfo.value?.data?.invoiceSize, context: context, ); }, icon: const Icon( FeatherIcons.printer, color: Colors.grey, ), visualDensity: const VisualDensity( horizontal: -4, vertical: -4, ), style: IconButton.styleFrom( padding: EdgeInsets.zero, ), ), const SizedBox(width: 8), businessInfo.when(data: (business) { return Row( children: [ IconButton( style: IconButton.styleFrom( padding: EdgeInsets.zero, visualDensity: const VisualDensity( horizontal: -4, vertical: -4, )), onPressed: () => PurchaseInvoicePDF.generatePurchaseDocument( currentTransaction, data, context, showPreview: true, ), icon: const Icon( Icons.picture_as_pdf, color: Colors.grey, ), ), IconButton( style: IconButton.styleFrom( padding: EdgeInsets.zero, visualDensity: const VisualDensity( horizontal: -4, vertical: -4, )), onPressed: () => PurchaseInvoicePDF.generatePurchaseDocument( currentTransaction, data, context, isShare: true), icon: const Icon( Icons.share_outlined, color: Colors.grey, ), ), ], ); }, error: (e, stack) { return Text(e.toString()); }, loading: () { return const Center( child: CircularProgressIndicator(), ); }), ], ); }, error: (e, stack) { return Text(e.toString()); }, loading: () { return Text(lang.S.of(context).loading); }), ], ), ], ), ), const Divider( height: 15, color: kBorderColor, ), const SizedBox(height: 10), ], ), ); }, ) : EmptyWidget( message: TextSpan(text: lang.S.of(context).noTransaction), ); }, error: (e, stack) { return Text(e.toString()); }, loading: () { return const Center(child: CircularProgressIndicator()); }), ), } else Center(child: PermitDenyWidget()), ], ), ), // bottomNavigationBar: ButtonGlobal( // iconWidget: null, // buttontext: lang.S.of(context).viewAll, // iconColor: Colors.white, // buttonDecoration: kButtonDecoration.copyWith(color: kMainColor), // onPressed: () { // Navigator.push(context, MaterialPageRoute(builder: (context)=>const CustomerAllTransactionScreen())); // }, // ), ), ); }); } } class ContactOptionsRow extends StatefulWidget { final Party party; const ContactOptionsRow({super.key, required this.party}); @override State createState() => _ContactOptionsRowState(); } class _ContactOptionsRowState extends State { int selectedIndex = -1; void _onButtonTap(int index) async { setState(() { selectedIndex = index; }); if (index == 0) { // Call functionality if (widget.party.phone == null) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(lang.S.of(context).phoneNotAvail)), ); return; } final Uri url = Uri.parse('tel:${widget.party.phone}'); bool t = await launchUrl(url); if (!t) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(lang.S.of(context).notLaunch)), ); } } else if (index == 1) { // SMS functionality if (widget.party.type != 'Supplier') { showDialog( context: context, builder: (context1) { return SmsConfirmationPopup( customerName: widget.party.name ?? '', phoneNumber: widget.party.phone ?? '', onCancel: () { Navigator.pop(context1); }, onSendSms: () { UrlLauncher.handleLaunchURL(context, 'sms:${widget.party.phone}', false); // EasyLoading.show(status: 'SMS Sending..'); // PartyRepository repo = PartyRepository(); // await repo.sendCustomerUdeSms(id: widget.party.id!, context: context); }, ); }, ); } else { if (widget.party.phone == null) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(lang.S.of(context).phoneNotAvail)), ); return; } UrlLauncher.handleLaunchURL( context, 'sms:${widget.party.phone}', false, ); } } else if (index == 2) { // Email functionality if (widget.party.email == null || !RegExp(r'^[^@]+@[^@]+\.[^@]+').hasMatch(widget.party.email!)) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Invalid email address.')), ); return; } UrlLauncher.handleLaunchURL(context, 'mailto:${widget.party.email}', true); } } Widget _buildContactButton(int index, IconData icon, String label) { final _theme = Theme.of(context); return Expanded( child: GestureDetector( onTap: () => _onButtonTap(index), child: Container( padding: const EdgeInsets.all(8), height: 90, decoration: BoxDecoration( color: selectedIndex == index ? kMainColor : kMainColor.withValues(alpha: 0.10), borderRadius: const BorderRadius.all(Radius.circular(10)), ), child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( icon, size: 20, color: selectedIndex == index ? kWhite : Colors.black, ), const SizedBox(height: 8), FittedBox( fit: BoxFit.scaleDown, child: Text( label, maxLines: 1, style: _theme.textTheme.bodyMedium?.copyWith( fontSize: 14, color: selectedIndex == index ? kWhite : Colors.black, ), ), ), ], ), ), ), ), ); } @override Widget build(BuildContext context) { return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ _buildContactButton(0, FeatherIcons.phone, 'Call'), const SizedBox(width: 18), _buildContactButton(1, FeatherIcons.messageSquare, 'Message'), const SizedBox(width: 18), _buildContactButton(2, FeatherIcons.mail, 'Email'), ], ); } } Widget keyValueWidget({required String title, required String value, required BuildContext context}) { final _theme = Theme.of(context); return Padding( padding: const EdgeInsets.only(bottom: 8), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Flexible( fit: FlexFit.tight, flex: 3, child: Text( '$title ', style: _theme.textTheme.bodyMedium?.copyWith( color: DAppColors.kNeutral700, ), ), ), SizedBox(width: 8), Flexible( fit: FlexFit.tight, flex: 4, child: Text( ': $value', style: _theme.textTheme.bodyMedium?.copyWith( color: kTitleColor, // fontSize: 15, ), ), ), ], ), ); }