payment page
This commit is contained in:
@@ -16,6 +16,7 @@ import 'package:worker/features/main/presentation/pages/main_scaffold.dart';
|
|||||||
import 'package:worker/features/orders/presentation/pages/order_detail_page.dart';
|
import 'package:worker/features/orders/presentation/pages/order_detail_page.dart';
|
||||||
import 'package:worker/features/orders/presentation/pages/orders_page.dart';
|
import 'package:worker/features/orders/presentation/pages/orders_page.dart';
|
||||||
import 'package:worker/features/orders/presentation/pages/payment_detail_page.dart';
|
import 'package:worker/features/orders/presentation/pages/payment_detail_page.dart';
|
||||||
|
import 'package:worker/features/orders/presentation/pages/payment_qr_page.dart';
|
||||||
import 'package:worker/features/orders/presentation/pages/payments_page.dart';
|
import 'package:worker/features/orders/presentation/pages/payments_page.dart';
|
||||||
import 'package:worker/features/products/presentation/pages/product_detail_page.dart';
|
import 'package:worker/features/products/presentation/pages/product_detail_page.dart';
|
||||||
import 'package:worker/features/products/presentation/pages/products_page.dart';
|
import 'package:worker/features/products/presentation/pages/products_page.dart';
|
||||||
@@ -176,6 +177,21 @@ class AppRouter {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
||||||
|
// Payment QR Route
|
||||||
|
GoRoute(
|
||||||
|
path: RouteNames.paymentQr,
|
||||||
|
name: RouteNames.paymentQr,
|
||||||
|
pageBuilder: (context, state) {
|
||||||
|
final orderId = state.uri.queryParameters['orderId'] ?? '';
|
||||||
|
final amountStr = state.uri.queryParameters['amount'] ?? '0';
|
||||||
|
final amount = double.tryParse(amountStr) ?? 0.0;
|
||||||
|
return MaterialPage(
|
||||||
|
key: state.pageKey,
|
||||||
|
child: PaymentQrPage(orderId: orderId, amount: amount),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
// Quotes Route
|
// Quotes Route
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: RouteNames.quotes,
|
path: RouteNames.quotes,
|
||||||
@@ -328,6 +344,7 @@ class RouteNames {
|
|||||||
static const String orderDetail = '/orders/:id';
|
static const String orderDetail = '/orders/:id';
|
||||||
static const String payments = '/payments';
|
static const String payments = '/payments';
|
||||||
static const String paymentDetail = '/payments/:id';
|
static const String paymentDetail = '/payments/:id';
|
||||||
|
static const String paymentQr = '/payment-qr';
|
||||||
|
|
||||||
// Projects & Quotes Routes
|
// Projects & Quotes Routes
|
||||||
static const String projects = '/projects';
|
static const String projects = '/projects';
|
||||||
|
|||||||
@@ -5,7 +5,9 @@ library;
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
import 'package:worker/core/constants/ui_constants.dart';
|
import 'package:worker/core/constants/ui_constants.dart';
|
||||||
|
import 'package:worker/core/router/app_router.dart';
|
||||||
import 'package:worker/core/theme/colors.dart';
|
import 'package:worker/core/theme/colors.dart';
|
||||||
|
|
||||||
/// Checkout Submit Button
|
/// Checkout Submit Button
|
||||||
@@ -103,16 +105,6 @@ class CheckoutSubmitButton extends StatelessWidget {
|
|||||||
duration: Duration(seconds: 2),
|
duration: Duration(seconds: 2),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
// Show order success message
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
const SnackBar(
|
|
||||||
content: Text('Đặt hàng thành công!'),
|
|
||||||
backgroundColor: AppColors.success,
|
|
||||||
duration: Duration(seconds: 2),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Navigate back after a short delay
|
// Navigate back after a short delay
|
||||||
Future.delayed(const Duration(milliseconds: 500), () {
|
Future.delayed(const Duration(milliseconds: 500), () {
|
||||||
@@ -120,5 +112,31 @@ class CheckoutSubmitButton extends StatelessWidget {
|
|||||||
context.pop();
|
context.pop();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
// Generate order ID (mock - replace with actual from backend)
|
||||||
|
final orderId = 'DH${DateTime.now().millisecondsSinceEpoch.toString().substring(7)}';
|
||||||
|
|
||||||
|
// Show order success message
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
const SnackBar(
|
||||||
|
content: Text('Đặt hàng thành công! Chuyển đến thanh toán...'),
|
||||||
|
backgroundColor: AppColors.success,
|
||||||
|
duration: Duration(seconds: 1),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Navigate to payment QR page after a short delay
|
||||||
|
Future.delayed(const Duration(milliseconds: 500), () {
|
||||||
|
if (context.mounted) {
|
||||||
|
context.pushNamed(
|
||||||
|
RouteNames.paymentQr,
|
||||||
|
queryParameters: {
|
||||||
|
'orderId': orderId,
|
||||||
|
'amount': total.toString(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
605
lib/features/orders/presentation/pages/payment_qr_page.dart
Normal file
605
lib/features/orders/presentation/pages/payment_qr_page.dart
Normal file
@@ -0,0 +1,605 @@
|
|||||||
|
/// Payment QR Page
|
||||||
|
///
|
||||||
|
/// QR code payment screen with bank transfer information.
|
||||||
|
/// Features:
|
||||||
|
/// - Payment amount display with minimum payment warning
|
||||||
|
/// - QR code for quick payment
|
||||||
|
/// - Bank transfer information with copy buttons
|
||||||
|
/// - Payment confirmation and proof upload buttons
|
||||||
|
/// - Countdown timer for payment
|
||||||
|
/// - Payment instructions modal
|
||||||
|
library;
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
|
||||||
|
import 'package:worker/core/constants/ui_constants.dart';
|
||||||
|
import 'package:worker/core/theme/colors.dart';
|
||||||
|
|
||||||
|
/// Payment QR Page
|
||||||
|
///
|
||||||
|
/// Displays QR code and bank transfer information for payment.
|
||||||
|
class PaymentQrPage extends HookConsumerWidget {
|
||||||
|
final String orderId;
|
||||||
|
final double amount;
|
||||||
|
|
||||||
|
const PaymentQrPage({
|
||||||
|
super.key,
|
||||||
|
required this.orderId,
|
||||||
|
required this.amount,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
// Countdown timer (15 minutes = 900 seconds)
|
||||||
|
final remainingSeconds = useState<int>(900);
|
||||||
|
final timer = useRef<Timer?>(null);
|
||||||
|
|
||||||
|
// Start countdown timer
|
||||||
|
useEffect(() {
|
||||||
|
timer.value = Timer.periodic(const Duration(seconds: 1), (t) {
|
||||||
|
if (remainingSeconds.value > 0) {
|
||||||
|
remainingSeconds.value--;
|
||||||
|
} else {
|
||||||
|
t.cancel();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return () {
|
||||||
|
timer.value?.cancel();
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Format timer display
|
||||||
|
final minutes = remainingSeconds.value ~/ 60;
|
||||||
|
final seconds = remainingSeconds.value % 60;
|
||||||
|
final timerDisplay =
|
||||||
|
'${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}';
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
backgroundColor: const Color(0xFFF4F6F8),
|
||||||
|
appBar: AppBar(
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
elevation: 0,
|
||||||
|
leading: IconButton(
|
||||||
|
icon: const Icon(Icons.arrow_back, color: Colors.black),
|
||||||
|
onPressed: () => context.pop(),
|
||||||
|
),
|
||||||
|
title: const Text(
|
||||||
|
'Thanh toán',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.black,
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
centerTitle: false,
|
||||||
|
actions: [
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.info_outline, color: Colors.black),
|
||||||
|
onPressed: () => _showInfoDialog(context),
|
||||||
|
),
|
||||||
|
const SizedBox(width: AppSpacing.sm),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
body: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
const SizedBox(height: AppSpacing.md),
|
||||||
|
|
||||||
|
// Payment Amount Card
|
||||||
|
_buildAmountCard(amount),
|
||||||
|
|
||||||
|
const SizedBox(height: AppSpacing.md),
|
||||||
|
|
||||||
|
// QR Code Card
|
||||||
|
_buildQrCodeCard(amount, orderId),
|
||||||
|
|
||||||
|
const SizedBox(height: AppSpacing.md),
|
||||||
|
|
||||||
|
// Bank Transfer Info Card
|
||||||
|
_buildBankInfoCard(context, orderId),
|
||||||
|
|
||||||
|
const SizedBox(height: AppSpacing.md),
|
||||||
|
|
||||||
|
// Action Buttons
|
||||||
|
_buildActionButtons(context),
|
||||||
|
|
||||||
|
const SizedBox(height: AppSpacing.md),
|
||||||
|
|
||||||
|
// Timer
|
||||||
|
_buildTimer(timerDisplay, remainingSeconds.value),
|
||||||
|
|
||||||
|
const SizedBox(height: AppSpacing.lg),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build payment amount card
|
||||||
|
Widget _buildAmountCard(double amount) {
|
||||||
|
return Container(
|
||||||
|
margin: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
|
||||||
|
padding: const EdgeInsets.all(AppSpacing.md),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(AppRadius.card),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black.withValues(alpha: 0.05),
|
||||||
|
blurRadius: 8,
|
||||||
|
offset: const Offset(0, 2),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
_formatCurrency(amount),
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 28,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: AppColors.primaryBlue,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
const Text(
|
||||||
|
'Số tiền cần thanh toán',
|
||||||
|
style: TextStyle(fontSize: 14, color: AppColors.grey500),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.all(12),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: const Color(0xFFFFF8E1),
|
||||||
|
borderRadius: BorderRadius.circular(AppRadius.card),
|
||||||
|
border: Border.all(color: const Color(0xFFFFD54F)),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
const Icon(Icons.info, color: AppColors.warning, size: 20),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
'Thanh toán không dưới 20%',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Colors.orange[900],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build QR code card
|
||||||
|
Widget _buildQrCodeCard(double amount, String orderId) {
|
||||||
|
// Generate QR code data URL
|
||||||
|
final qrData = Uri.encodeComponent(
|
||||||
|
'https://eurotile.com/payment/$orderId?amount=$amount');
|
||||||
|
final qrUrl =
|
||||||
|
'https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=$qrData';
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
margin: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
|
||||||
|
padding: const EdgeInsets.all(AppSpacing.md),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(AppRadius.card),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black.withValues(alpha: 0.05),
|
||||||
|
blurRadius: 8,
|
||||||
|
offset: const Offset(0, 2),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
const Text(
|
||||||
|
'Quét mã QR để thanh toán',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Color(0xFF212121),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: AppSpacing.md),
|
||||||
|
Container(
|
||||||
|
width: 220,
|
||||||
|
height: 220,
|
||||||
|
padding: const EdgeInsets.all(10),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(AppRadius.card),
|
||||||
|
border: Border.all(color: const Color(0xFFE2E8F0)),
|
||||||
|
),
|
||||||
|
child: Image.network(
|
||||||
|
qrUrl,
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
errorBuilder: (context, error, stackTrace) {
|
||||||
|
return const Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(Icons.qr_code, size: 80, color: AppColors.grey500),
|
||||||
|
SizedBox(height: 8),
|
||||||
|
Text(
|
||||||
|
'Không thể tải mã QR',
|
||||||
|
style: TextStyle(fontSize: 12, color: AppColors.grey500),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: AppSpacing.md),
|
||||||
|
const Text(
|
||||||
|
'Quét mã QR bằng ứng dụng ngân hàng để thanh toán nhanh chóng',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(fontSize: 14, color: AppColors.grey500),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build bank transfer info card
|
||||||
|
Widget _buildBankInfoCard(BuildContext context, String orderId) {
|
||||||
|
return Container(
|
||||||
|
margin: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
|
||||||
|
padding: const EdgeInsets.all(AppSpacing.md),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(AppRadius.card),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black.withValues(alpha: 0.05),
|
||||||
|
blurRadius: 8,
|
||||||
|
offset: const Offset(0, 2),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const Text(
|
||||||
|
'Thông tin chuyển khoản',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Color(0xFF212121),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: AppSpacing.md),
|
||||||
|
|
||||||
|
// Bank Name
|
||||||
|
_buildInfoRow(
|
||||||
|
context: context,
|
||||||
|
label: 'Ngân hàng:',
|
||||||
|
value: 'BIDV',
|
||||||
|
),
|
||||||
|
|
||||||
|
const Divider(height: 24),
|
||||||
|
|
||||||
|
// Account Number
|
||||||
|
_buildInfoRow(
|
||||||
|
context: context,
|
||||||
|
label: 'Số tài khoản:',
|
||||||
|
value: '19036810704016',
|
||||||
|
),
|
||||||
|
|
||||||
|
const Divider(height: 24),
|
||||||
|
|
||||||
|
// Account Holder
|
||||||
|
_buildInfoRow(
|
||||||
|
context: context,
|
||||||
|
label: 'Chủ tài khoản:',
|
||||||
|
value: 'CÔNG TY EUROTILE',
|
||||||
|
),
|
||||||
|
|
||||||
|
const Divider(height: 24),
|
||||||
|
|
||||||
|
// Transfer Content
|
||||||
|
_buildInfoRow(
|
||||||
|
context: context,
|
||||||
|
label: 'Nội dung:',
|
||||||
|
value: '$orderId La Nguyen Quynh',
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: AppSpacing.md),
|
||||||
|
|
||||||
|
// Note
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.all(12),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: const Color(0xFFE3F2FD),
|
||||||
|
borderRadius: BorderRadius.circular(AppRadius.card),
|
||||||
|
border: Border.all(color: const Color(0xFF90CAF9)),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const Icon(Icons.lightbulb_outline,
|
||||||
|
color: AppColors.primaryBlue, size: 20),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Expanded(
|
||||||
|
child: RichText(
|
||||||
|
text: TextSpan(
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
color: Color(0xFF1565C0),
|
||||||
|
height: 1.4,
|
||||||
|
),
|
||||||
|
children: [
|
||||||
|
const TextSpan(
|
||||||
|
text: 'Lưu ý: ',
|
||||||
|
style: TextStyle(fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
const TextSpan(
|
||||||
|
text:
|
||||||
|
'Vui lòng ghi đúng nội dung chuyển khoản để đơn hàng được xử lý nhanh chóng.',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build info row with copy button
|
||||||
|
Widget _buildInfoRow({
|
||||||
|
required BuildContext context,
|
||||||
|
required String label,
|
||||||
|
required String value,
|
||||||
|
}) {
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
flex: 2,
|
||||||
|
child: Text(
|
||||||
|
label,
|
||||||
|
style: const TextStyle(fontSize: 14, color: AppColors.grey500),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
flex: 3,
|
||||||
|
child: Text(
|
||||||
|
value,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Color(0xFF212121),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.copy, size: 20, color: AppColors.primaryBlue),
|
||||||
|
onPressed: () => _copyToClipboard(context, value),
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
constraints: const BoxConstraints(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build action buttons
|
||||||
|
Widget _buildActionButtons(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
// Confirmed Payment Button
|
||||||
|
Expanded(
|
||||||
|
child: OutlinedButton.icon(
|
||||||
|
onPressed: () => _confirmPayment(context),
|
||||||
|
icon: const Icon(Icons.check, size: 20),
|
||||||
|
label: const Text(
|
||||||
|
'Đã thanh toán',
|
||||||
|
style: TextStyle(fontSize: 15, fontWeight: FontWeight.w600),
|
||||||
|
),
|
||||||
|
style: OutlinedButton.styleFrom(
|
||||||
|
foregroundColor: AppColors.primaryBlue,
|
||||||
|
side: const BorderSide(color: AppColors.primaryBlue, width: 1.5),
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(AppRadius.button),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: AppSpacing.sm),
|
||||||
|
|
||||||
|
// Upload Proof Button
|
||||||
|
Expanded(
|
||||||
|
child: ElevatedButton.icon(
|
||||||
|
onPressed: () => _uploadProof(context),
|
||||||
|
icon: const Icon(Icons.camera_alt, size: 20),
|
||||||
|
label: const Text(
|
||||||
|
'Upload bill',
|
||||||
|
style: TextStyle(fontSize: 15, fontWeight: FontWeight.w600),
|
||||||
|
),
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: AppColors.primaryBlue,
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||||
|
elevation: 0,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(AppRadius.button),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build countdown timer
|
||||||
|
Widget _buildTimer(String timerDisplay, int remainingSeconds) {
|
||||||
|
return Container(
|
||||||
|
margin: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
|
||||||
|
padding: const EdgeInsets.all(12),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(AppRadius.card),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const Icon(Icons.schedule, size: 18, color: AppColors.grey500),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
const Text(
|
||||||
|
'Thời gian thanh toán: ',
|
||||||
|
style: TextStyle(fontSize: 14, color: AppColors.grey500),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
timerDisplay,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: remainingSeconds < 300
|
||||||
|
? AppColors.danger
|
||||||
|
: AppColors.grey900,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Copy text to clipboard
|
||||||
|
void _copyToClipboard(BuildContext context, String text) {
|
||||||
|
Clipboard.setData(ClipboardData(text: text));
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Text('Đã sao chép: $text'),
|
||||||
|
duration: const Duration(seconds: 1),
|
||||||
|
behavior: SnackBarBehavior.floating,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Show payment info dialog
|
||||||
|
void _showInfoDialog(BuildContext context) {
|
||||||
|
showDialog<void>(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => AlertDialog(
|
||||||
|
title: const Text('Hướng dẫn thanh toán'),
|
||||||
|
content: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const Text(
|
||||||
|
'Đây là nội dung hướng dẫn sử dụng cho tính năng Thanh toán:',
|
||||||
|
style: TextStyle(fontSize: 14),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
_buildInfoItem('Quét mã QR bằng app ngân hàng hoặc ví điện tử'),
|
||||||
|
_buildInfoItem('Chuyển khoản theo thông tin được cung cấp'),
|
||||||
|
_buildInfoItem('Ghi đúng nội dung chuyển khoản'),
|
||||||
|
_buildInfoItem('Upload hóa đơn sau khi chuyển khoản'),
|
||||||
|
_buildInfoItem('Thanh toán tối thiểu 20% giá trị đơn hàng'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
child: const Text('Đóng'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build info item for dialog
|
||||||
|
Widget _buildInfoItem(String text) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 8),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const Text('• ', style: TextStyle(fontSize: 14)),
|
||||||
|
Expanded(
|
||||||
|
child: Text(text, style: const TextStyle(fontSize: 14)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Confirm payment
|
||||||
|
void _confirmPayment(BuildContext context) {
|
||||||
|
showDialog<void>(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => AlertDialog(
|
||||||
|
title: const Text('Xác nhận thanh toán'),
|
||||||
|
content: const Text(
|
||||||
|
'Bạn đã hoàn tất thanh toán cho đơn hàng này?',
|
||||||
|
style: TextStyle(fontSize: 14),
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
child: const Text('Chưa'),
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
const SnackBar(
|
||||||
|
content: Text('Đã xác nhận thanh toán!'),
|
||||||
|
backgroundColor: AppColors.success,
|
||||||
|
duration: Duration(seconds: 2),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
// Navigate back after delay
|
||||||
|
Future.delayed(const Duration(milliseconds: 500), () {
|
||||||
|
if (context.mounted) {
|
||||||
|
context.pop();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: AppColors.primaryBlue,
|
||||||
|
),
|
||||||
|
child: const Text('Đã thanh toán'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Upload payment proof
|
||||||
|
void _uploadProof(BuildContext context) {
|
||||||
|
// TODO: Implement image picker and upload
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
const SnackBar(
|
||||||
|
content: Text('Tính năng upload bill đang được phát triển'),
|
||||||
|
duration: Duration(seconds: 2),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Format currency
|
||||||
|
String _formatCurrency(double amount) {
|
||||||
|
return '${amount.toStringAsFixed(0).replaceAllMapped(
|
||||||
|
RegExp(r'(\d)(?=(\d{3})+(?!\d))'),
|
||||||
|
(Match m) => '${m[1]}.',
|
||||||
|
)}₫';
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user