25-7-28-2
This commit is contained in:
610
lib/views/home/my_wallet/walet_captain.dart
Executable file
610
lib/views/home/my_wallet/walet_captain.dart
Executable file
@@ -0,0 +1,610 @@
|
||||
import 'package:local_auth/local_auth.dart';
|
||||
import 'package:sefer_driver/constant/links.dart';
|
||||
import 'package:sefer_driver/controller/functions/crud.dart';
|
||||
import 'package:sefer_driver/controller/functions/tts.dart';
|
||||
import 'package:sefer_driver/views/home/my_wallet/payment_history_driver_page.dart';
|
||||
import 'package:sefer_driver/views/widgets/error_snakbar.dart';
|
||||
import 'package:sefer_driver/views/widgets/mydialoug.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:sefer_driver/constant/box_name.dart';
|
||||
import 'package:sefer_driver/constant/colors.dart';
|
||||
import 'package:sefer_driver/constant/info.dart';
|
||||
import 'package:sefer_driver/constant/style.dart';
|
||||
import 'package:sefer_driver/controller/home/payment/captain_wallet_controller.dart';
|
||||
import 'package:sefer_driver/main.dart';
|
||||
import 'package:sefer_driver/views/widgets/elevated_btn.dart';
|
||||
import 'package:sefer_driver/views/widgets/my_textField.dart';
|
||||
import 'package:sefer_driver/views/widgets/mycircular.dart';
|
||||
|
||||
import '../../../controller/payment/driver_payment_controller.dart';
|
||||
import '../../widgets/my_scafold.dart';
|
||||
import 'card_wallet_widget.dart';
|
||||
import 'points_captain.dart';
|
||||
import 'transfer_budget_page.dart';
|
||||
import 'weekly_payment_page.dart';
|
||||
|
||||
class WalletCaptainRefactored extends StatelessWidget {
|
||||
WalletCaptainRefactored({super.key});
|
||||
|
||||
final CaptainWalletController captainWalletController =
|
||||
Get.put(CaptainWalletController());
|
||||
|
||||
// دالة مساعدة لتحديد لون خلفية النقاط
|
||||
Color _getPointsColor(String pointsStr) {
|
||||
final points = double.tryParse(pointsStr) ?? 0.0;
|
||||
if (points < -30000) {
|
||||
return AppColor.redColor;
|
||||
} else if (points < 0 && points >= -30000) {
|
||||
return AppColor.yellowColor;
|
||||
} else {
|
||||
return AppColor.greenColor;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
captainWalletController.refreshCaptainWallet();
|
||||
return MyScafolld(
|
||||
title: 'Driver Wallet'.tr,
|
||||
isleading: true,
|
||||
action: IconButton(
|
||||
icon: const Icon(Icons.refresh),
|
||||
onPressed: () => captainWalletController.refreshCaptainWallet(),
|
||||
tooltip: 'Refresh'.tr,
|
||||
),
|
||||
body: [
|
||||
GetBuilder<CaptainWalletController>(
|
||||
builder: (controller) {
|
||||
if (controller.isLoading) {
|
||||
return const MyCircularProgressIndicator();
|
||||
}
|
||||
return SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
_buildTotalPointsSection(context, controller),
|
||||
const SizedBox(height: 16),
|
||||
const CardSeferWalletDriver(), // This can be redesigned if needed
|
||||
const SizedBox(height: 16),
|
||||
_buildWalletDetailsCard(context, controller),
|
||||
const SizedBox(height: 24),
|
||||
_buildPromoSection(controller),
|
||||
const SizedBox(height: 24),
|
||||
_buildNavigationButtons(),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/// القسم العلوي لعرض النقاط الإجمالية
|
||||
Widget _buildTotalPointsSection(
|
||||
BuildContext context, CaptainWalletController controller) {
|
||||
return Card(
|
||||
elevation: 4,
|
||||
color: _getPointsColor(controller.totalPoints.toString()),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
showCupertinoDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) => CupertinoAlertDialog(
|
||||
title: Text('Info'.tr),
|
||||
content: Text(
|
||||
'The 30000 points equal 30000 S.P for you \nSo go and gain your money'
|
||||
.tr),
|
||||
actions: [
|
||||
CupertinoDialogAction(
|
||||
child: Text("OK".tr),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 8),
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
'${'Total Points is'.tr} 💎',
|
||||
style: AppStyle.headTitle2
|
||||
.copyWith(color: Colors.white, fontWeight: FontWeight.bold),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
controller.totalPoints.toString(),
|
||||
style: AppStyle.headTitle2.copyWith(
|
||||
color: Colors.white,
|
||||
fontSize: 28,
|
||||
fontWeight: FontWeight.w900),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
if (double.parse(controller.totalPoints.toString()) < -30000)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 12.0),
|
||||
child: CupertinoButton(
|
||||
color: Colors.white,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
onPressed: () {
|
||||
// Add your charge account logic here
|
||||
},
|
||||
child: Text(
|
||||
'Charge your Account'.tr,
|
||||
style: TextStyle(
|
||||
color: AppColor.redColor,
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// بطاقة لعرض تفاصيل المحفظة وخيارات الشراء
|
||||
Widget _buildWalletDetailsCard(
|
||||
BuildContext context, CaptainWalletController controller) {
|
||||
return Card(
|
||||
elevation: 4,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
_BudgetInfoRow(
|
||||
title: 'Total Budget from trips is '.tr,
|
||||
amount: controller.totalAmount,
|
||||
onTap: () {
|
||||
Get.snackbar(
|
||||
icon: InkWell(
|
||||
onTap: () async => await Get.put(TextToSpeechController())
|
||||
.speakText(
|
||||
'This amount for all trip I get from Passengers'
|
||||
.tr),
|
||||
child: const Icon(Icons.headphones)),
|
||||
'${'Total Amount:'.tr} ${controller.totalAmount} ${'S.P'.tr}',
|
||||
'This amount for all trip I get from Passengers'.tr,
|
||||
duration: const Duration(seconds: 6),
|
||||
backgroundColor: AppColor.yellowColor,
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
);
|
||||
},
|
||||
),
|
||||
const Divider(height: 32),
|
||||
_BudgetInfoRow(
|
||||
title: 'Total Budget from trips by\nCredit card is '.tr,
|
||||
amount: controller.totalAmountVisa,
|
||||
onTap: () {
|
||||
Get.snackbar(
|
||||
icon: InkWell(
|
||||
onTap: () async => await Get.find<TextToSpeechController>()
|
||||
.speakText(
|
||||
'This amount for all trip I get from Passengers and Collected For me in'
|
||||
.tr +
|
||||
' SAFAR Wallet'.tr),
|
||||
child: const Icon(Icons.headphones),
|
||||
),
|
||||
'${'Total Amount:'.tr} ${controller.totalAmountVisa} ${'S.P'.tr}',
|
||||
'This amount for all trip I get from Passengers and Collected For me in'
|
||||
.tr +
|
||||
' ${AppInformation.appName} Wallet'.tr,
|
||||
duration: const Duration(seconds: 6),
|
||||
backgroundColor: AppColor.redColor,
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
_buildBuyPointsButton(controller),
|
||||
const SizedBox(height: 16),
|
||||
// _buildTransferBudgetButton(controller), // Uncomment if needed
|
||||
_buildPurchaseInstructions(),
|
||||
const SizedBox(height: 8),
|
||||
_buildPointsOptions(),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// قسم العروض الترويجية
|
||||
Widget _buildPromoSection(CaptainWalletController controller) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text("Today's Promo".tr, style: AppStyle.headTitle),
|
||||
const SizedBox(height: 10),
|
||||
_PromoProgressCard(
|
||||
title: 'Morning Promo'.tr,
|
||||
timePromo: 'Morning Promo',
|
||||
count: (controller.walletDate['message'][0]['morning_count']),
|
||||
maxCount: 5,
|
||||
description:
|
||||
"this is count of your all trips in the morning promo today from 7:00am to 10:00am"
|
||||
.tr,
|
||||
controller: controller,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_PromoProgressCard(
|
||||
title: 'Afternoon Promo'.tr,
|
||||
timePromo: 'Afternoon Promo',
|
||||
count: (controller.walletDate['message'][0]['afternoon_count']),
|
||||
maxCount: 5,
|
||||
description:
|
||||
"this is count of your all trips in the Afternoon promo today from 3:00pm to 6:00 pm"
|
||||
.tr,
|
||||
controller: controller,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/// أزرار التنقل السفلية
|
||||
Widget _buildNavigationButtons() {
|
||||
return Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: MyElevatedButton(
|
||||
kolor: AppColor.blueColor,
|
||||
title: 'Payment History'.tr,
|
||||
onPressed: () async {
|
||||
await Get.put(DriverWalletHistoryController())
|
||||
.getArchivePayment();
|
||||
Get.to(() => const PaymentHistoryDriverPage(),
|
||||
transition: Transition.size);
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: MyElevatedButton(
|
||||
kolor: AppColor.blueColor,
|
||||
title: 'Weekly Budget'.tr,
|
||||
onPressed: () async {
|
||||
await Get.put(DriverWalletHistoryController())
|
||||
.getWeekllyArchivePayment();
|
||||
Get.to(() => const WeeklyPaymentPage(),
|
||||
transition: Transition.size);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
// --- حافظت على هذه الدوال كما هي لأنها تحتوي على منطق مهم ---
|
||||
Widget _buildBuyPointsButton(CaptainWalletController controller) {
|
||||
return MyElevatedButton(
|
||||
title: 'You can buy points from your budget'.tr,
|
||||
onPressed: () {
|
||||
Get.defaultDialog(
|
||||
title: 'Pay from my budget'.tr,
|
||||
content: Form(
|
||||
key: controller.formKey,
|
||||
child: MyTextForm(
|
||||
controller: controller.amountFromBudgetController,
|
||||
label:
|
||||
'${'You have in account'.tr} ${controller.totalAmountVisa}',
|
||||
hint: '${'You have in account'.tr} ${controller.totalAmountVisa}',
|
||||
type: TextInputType.number,
|
||||
),
|
||||
),
|
||||
confirm: MyElevatedButton(
|
||||
title: 'Pay'.tr,
|
||||
onPressed: () async {
|
||||
bool isAvailable =
|
||||
await LocalAuthentication().isDeviceSupported();
|
||||
if (isAvailable) {
|
||||
// Authenticate the user
|
||||
bool didAuthenticate = await LocalAuthentication().authenticate(
|
||||
localizedReason:
|
||||
'Use Touch ID or Face ID to confirm payment'.tr,
|
||||
options: const AuthenticationOptions(
|
||||
biometricOnly: true,
|
||||
sensitiveTransaction: true,
|
||||
));
|
||||
if (didAuthenticate) {
|
||||
if (double.parse(controller.amountFromBudgetController.text) <
|
||||
double.parse(controller.totalAmountVisa)) {
|
||||
await controller.payFromBudget();
|
||||
} else {
|
||||
Get.back();
|
||||
|
||||
mySnackeBarError('Your Budget less than needed'.tr);
|
||||
}
|
||||
} else {
|
||||
// Authentication failed, handle accordingly
|
||||
MyDialog().getDialog('Authentication failed'.tr, ''.tr, () {
|
||||
Get.back();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
MyDialog().getDialog('Biometric Authentication'.tr,
|
||||
'You should use Touch ID or Face ID to confirm payment'.tr,
|
||||
() {
|
||||
Get.back();
|
||||
});
|
||||
}
|
||||
},
|
||||
),
|
||||
cancel: MyElevatedButton(
|
||||
title: 'Cancel'.tr,
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPurchaseInstructions() {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.accentColor.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: AppColor.accentColor.withOpacity(0.3)),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
"You can purchase a budget to enable online access through the options listed below"
|
||||
.tr,
|
||||
textAlign: TextAlign.center,
|
||||
style: AppStyle.title.copyWith(fontSize: 14),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPointsOptions() {
|
||||
return SizedBox(
|
||||
height: Get.height * 0.19,
|
||||
child: ListView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
children: [
|
||||
PointsCaptain(
|
||||
kolor: AppColor.greyColor,
|
||||
pricePoint: 10000,
|
||||
countPoint: '10000'),
|
||||
PointsCaptain(
|
||||
kolor: AppColor.bronze, pricePoint: 20000, countPoint: '21000'),
|
||||
PointsCaptain(
|
||||
kolor: AppColor.goldenBronze,
|
||||
pricePoint: 40000,
|
||||
countPoint: '45000'),
|
||||
PointsCaptain(
|
||||
kolor: AppColor.gold, pricePoint: 100000, countPoint: '110000'),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// ويدجت مُحسّن لعرض صف معلومات الرصيد
|
||||
class _BudgetInfoRow extends StatelessWidget {
|
||||
final String title;
|
||||
final String amount;
|
||||
final VoidCallback onTap;
|
||||
|
||||
const _BudgetInfoRow({
|
||||
required this.title,
|
||||
required this.amount,
|
||||
required this.onTap,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
title,
|
||||
style: AppStyle.title.copyWith(fontSize: 16),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.accentColor.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
child: Text(
|
||||
'$amount ${'S.P'.tr}',
|
||||
style:
|
||||
const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// ويدجت مُحسّن لعرض بطاقة العرض الترويجي
|
||||
class _PromoProgressCard extends StatelessWidget {
|
||||
final String title;
|
||||
final String timePromo;
|
||||
final int count;
|
||||
final int maxCount;
|
||||
final String description;
|
||||
final CaptainWalletController controller;
|
||||
|
||||
const _PromoProgressCard({
|
||||
required this.title,
|
||||
required this.timePromo,
|
||||
required this.count,
|
||||
required this.maxCount,
|
||||
required this.description,
|
||||
required this.controller,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Card(
|
||||
elevation: 2,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
MyDialog().getDialog(title, description, () async {
|
||||
if (count >= maxCount) {
|
||||
controller.addDriverWalletFromPromo(timePromo, 50);
|
||||
}
|
||||
Get.back();
|
||||
});
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(title,
|
||||
style:
|
||||
AppStyle.title.copyWith(fontWeight: FontWeight.bold)),
|
||||
Text(
|
||||
'$count / $maxCount',
|
||||
style: AppStyle.title.copyWith(color: AppColor.blueColor),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
child: LinearProgressIndicator(
|
||||
minHeight: 12,
|
||||
value: count / maxCount,
|
||||
backgroundColor: AppColor.accentColor.withOpacity(0.2),
|
||||
color: count >= maxCount
|
||||
? AppColor.greenColor
|
||||
: AppColor.blueColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// --- الدوال والويدجتس الخاصة بالدفع في سوريا تبقى كما هي ---
|
||||
// This function is a placeholder for adding Syrian payment methods.
|
||||
// You would implement the UI and logic for mobile wallets or other local options here.
|
||||
Future<dynamic> addSyrianPaymentMethod(
|
||||
CaptainWalletController captainWalletController) {
|
||||
return Get.defaultDialog(
|
||||
title: "Insert Payment Details".tr,
|
||||
content: Form(
|
||||
key: captainWalletController.formKeyAccount,
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
"Insert your mobile wallet details to receive your money weekly"
|
||||
.tr),
|
||||
MyTextForm(
|
||||
controller: captainWalletController
|
||||
.cardBank, // Re-using for mobile number
|
||||
label: "Insert mobile wallet number".tr,
|
||||
hint: '0912 345 678',
|
||||
type: TextInputType.phone),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
MyDropDownSyria() // Dropdown for Syrian providers
|
||||
],
|
||||
)),
|
||||
confirm: MyElevatedButton(
|
||||
title: 'Ok'.tr,
|
||||
onPressed: () async {
|
||||
if (captainWalletController.formKeyAccount.currentState!
|
||||
.validate()) {
|
||||
Get.back();
|
||||
// Replace with your actual API endpoint and payload for Syria
|
||||
var res =
|
||||
await CRUD().post(link: AppLink.updateAccountBank, payload: {
|
||||
"paymentProvider":
|
||||
Get.find<SyrianPayoutController>().dropdownValue.toString(),
|
||||
"accountNumber":
|
||||
captainWalletController.cardBank.text.toString(),
|
||||
"id": box.read(BoxName.driverID).toString()
|
||||
});
|
||||
print('res: $res');
|
||||
if (res != 'failure') {
|
||||
mySnackbarSuccess('Payment details added successfully'.tr);
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
// A new GetX controller for the Syrian payout dropdown
|
||||
class SyrianPayoutController extends GetxController {
|
||||
String dropdownValue = 'syriatel';
|
||||
|
||||
void changeValue(String? newValue) {
|
||||
if (newValue != null) {
|
||||
dropdownValue = newValue;
|
||||
update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A new Dropdown widget for Syrian mobile wallet providers
|
||||
class MyDropDownSyria extends StatelessWidget {
|
||||
const MyDropDownSyria({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Get.put(SyrianPayoutController());
|
||||
return GetBuilder<SyrianPayoutController>(builder: (controller) {
|
||||
return DropdownButton<String>(
|
||||
value: controller.dropdownValue,
|
||||
icon: const Icon(Icons.arrow_drop_down),
|
||||
elevation: 16,
|
||||
isExpanded: true,
|
||||
style: const TextStyle(color: Colors.deepPurple),
|
||||
underline: Container(
|
||||
height: 2,
|
||||
color: Colors.deepPurpleAccent,
|
||||
),
|
||||
onChanged: (String? newValue) {
|
||||
controller.changeValue(newValue);
|
||||
},
|
||||
items: <String>['syriatel', 'mtn']
|
||||
.map<DropdownMenuItem<String>>((String value) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: value,
|
||||
child: Text(value.tr),
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user