722 lines
35 KiB
Dart
722 lines
35 KiB
Dart
|
|
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<CustomerDetails> createState() => _CustomerDetailsState();
|
||
|
|
}
|
||
|
|
|
||
|
|
class _CustomerDetailsState extends ConsumerState<CustomerDetails> {
|
||
|
|
@override
|
||
|
|
void initState() {
|
||
|
|
super.initState();
|
||
|
|
}
|
||
|
|
|
||
|
|
Future<void> 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<ContactOptionsRow> createState() => _ContactOptionsRowState();
|
||
|
|
}
|
||
|
|
|
||
|
|
class _ContactOptionsRowState extends State<ContactOptionsRow> {
|
||
|
|
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,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
);
|
||
|
|
}
|