update theme
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -33,11 +33,12 @@ class OrderSuccessPage extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
final now = DateTime.now();
|
||||
final dateFormat = DateFormat('dd/MM/yyyy HH:mm');
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
backgroundColor: colorScheme.surface,
|
||||
body: SafeArea(
|
||||
child: Center(
|
||||
child: SingleChildScrollView(
|
||||
@@ -67,10 +68,10 @@ class OrderSuccessPage extends StatelessWidget {
|
||||
isNegotiation
|
||||
? 'Gửi yêu cầu thành công!'
|
||||
: 'Tạo đơn hàng thành công!',
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Color(0xFF212121),
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
@@ -82,9 +83,9 @@ class OrderSuccessPage extends StatelessWidget {
|
||||
isNegotiation
|
||||
? 'Chúng tôi sẽ liên hệ với bạn để đàm phán giá trong vòng 24 giờ.'
|
||||
: 'Cảm ơn bạn đã đặt hàng. Chúng tôi sẽ liên hệ xác nhận trong vòng 24 giờ.',
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: AppColors.grey500,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
@@ -96,7 +97,7 @@ class OrderSuccessPage extends StatelessWidget {
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.all(AppSpacing.md),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFF4F6F8),
|
||||
color: colorScheme.surfaceContainerLowest,
|
||||
borderRadius: BorderRadius.circular(AppRadius.card),
|
||||
),
|
||||
child: Column(
|
||||
@@ -104,20 +105,20 @@ class OrderSuccessPage extends StatelessWidget {
|
||||
// Order Number
|
||||
Column(
|
||||
children: [
|
||||
const Text(
|
||||
Text(
|
||||
'Mã đơn hàng',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: AppColors.grey500,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
orderNumber,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppColors.primaryBlue,
|
||||
color: colorScheme.primary,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -127,6 +128,7 @@ class OrderSuccessPage extends StatelessWidget {
|
||||
|
||||
// Order Date
|
||||
_buildInfoRow(
|
||||
context,
|
||||
'Ngày đặt',
|
||||
dateFormat.format(now),
|
||||
),
|
||||
@@ -136,12 +138,13 @@ class OrderSuccessPage extends StatelessWidget {
|
||||
// Total Amount
|
||||
if (total != null)
|
||||
_buildInfoRow(
|
||||
context,
|
||||
'Tổng tiền',
|
||||
_formatCurrency(total!),
|
||||
valueStyle: const TextStyle(
|
||||
valueStyle: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Color(0xFF212121),
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -150,6 +153,7 @@ class OrderSuccessPage extends StatelessWidget {
|
||||
// Payment Method
|
||||
if (paymentMethod != null && !isNegotiation)
|
||||
_buildInfoRow(
|
||||
context,
|
||||
'Phương thức thanh toán',
|
||||
paymentMethod!,
|
||||
),
|
||||
@@ -159,14 +163,13 @@ class OrderSuccessPage extends StatelessWidget {
|
||||
|
||||
// Status
|
||||
_buildInfoRow(
|
||||
context,
|
||||
'Trạng thái',
|
||||
isNegotiation ? 'Chờ đàm phán' : 'Chờ xác nhận',
|
||||
valueStyle: TextStyle(
|
||||
valueStyle: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: isNegotiation
|
||||
? AppColors.warning
|
||||
: AppColors.warning,
|
||||
color: AppColors.warning,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -195,8 +198,8 @@ class OrderSuccessPage extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColors.primaryBlue,
|
||||
foregroundColor: Colors.white,
|
||||
backgroundColor: colorScheme.primary,
|
||||
foregroundColor: colorScheme.onPrimary,
|
||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
elevation: 0,
|
||||
shape: RoundedRectangleBorder(
|
||||
@@ -225,9 +228,9 @@ class OrderSuccessPage extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
style: OutlinedButton.styleFrom(
|
||||
foregroundColor: AppColors.grey900,
|
||||
foregroundColor: colorScheme.onSurface,
|
||||
side: BorderSide(
|
||||
color: AppColors.grey100,
|
||||
color: colorScheme.outlineVariant,
|
||||
width: 1.5,
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
@@ -247,26 +250,29 @@ class OrderSuccessPage extends StatelessWidget {
|
||||
|
||||
/// Build info row
|
||||
Widget _buildInfoRow(
|
||||
BuildContext context,
|
||||
String label,
|
||||
String value, {
|
||||
TextStyle? valueStyle,
|
||||
}) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
label,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: AppColors.grey500,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
value,
|
||||
style: valueStyle ??
|
||||
const TextStyle(
|
||||
TextStyle(
|
||||
fontSize: 14,
|
||||
color: Color(0xFF212121),
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -50,24 +50,25 @@ class _OrdersPageState extends ConsumerState<OrdersPage> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
final ordersAsync = ref.watch(ordersProvider);
|
||||
final selectedStatus = ref.watch(selectedOrderStatusProvider);
|
||||
final searchQuery = ref.watch(orderSearchQueryProvider);
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: const Color(0xFFF4F6F8),
|
||||
backgroundColor: colorScheme.surfaceContainerLowest,
|
||||
appBar: AppBar(
|
||||
leading: IconButton(
|
||||
icon: const FaIcon(FontAwesomeIcons.arrowLeft, color: Colors.black, size: 20),
|
||||
icon: FaIcon(FontAwesomeIcons.arrowLeft, color: colorScheme.onSurface, size: 20),
|
||||
onPressed: () => context.pop(),
|
||||
),
|
||||
title: const Text(
|
||||
title: Text(
|
||||
'Danh sách đơn hàng',
|
||||
style: TextStyle(color: Colors.black),
|
||||
style: TextStyle(color: colorScheme.onSurface),
|
||||
),
|
||||
elevation: AppBarSpecs.elevation,
|
||||
backgroundColor: AppColors.white,
|
||||
foregroundColor: AppColors.grey900,
|
||||
backgroundColor: colorScheme.surface,
|
||||
foregroundColor: colorScheme.onSurface,
|
||||
centerTitle: false,
|
||||
actions: const [SizedBox(width: AppSpacing.sm)],
|
||||
),
|
||||
@@ -82,9 +83,9 @@ class _OrdersPageState extends ConsumerState<OrdersPage> {
|
||||
pinned: true,
|
||||
delegate: _SearchBarDelegate(
|
||||
child: Container(
|
||||
color: const Color(0xFFF4F6F8),
|
||||
color: colorScheme.surfaceContainerLowest,
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: _buildSearchBar(),
|
||||
child: _buildSearchBar(colorScheme),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -94,8 +95,8 @@ class _OrdersPageState extends ConsumerState<OrdersPage> {
|
||||
pinned: true,
|
||||
delegate: _FilterPillsDelegate(
|
||||
child: Container(
|
||||
color: const Color(0xFFF4F6F8),
|
||||
child: _buildFilterPills(selectedStatus),
|
||||
color: colorScheme.surfaceContainerLowest,
|
||||
child: _buildFilterPills(selectedStatus, colorScheme),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -134,7 +135,7 @@ class _OrdersPageState extends ConsumerState<OrdersPage> {
|
||||
});
|
||||
|
||||
if (filtered.isEmpty) {
|
||||
return _buildEmptyState();
|
||||
return _buildEmptyState(colorScheme);
|
||||
}
|
||||
return SliverList(
|
||||
delegate: SliverChildBuilderDelegate((context, index) {
|
||||
@@ -149,7 +150,7 @@ class _OrdersPageState extends ConsumerState<OrdersPage> {
|
||||
);
|
||||
},
|
||||
loading: () => _buildLoadingState(),
|
||||
error: (error, stack) => _buildErrorState(error),
|
||||
error: (error, stack) => _buildErrorState(error, colorScheme),
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -159,14 +160,14 @@ class _OrdersPageState extends ConsumerState<OrdersPage> {
|
||||
}
|
||||
|
||||
/// Build search bar
|
||||
Widget _buildSearchBar() {
|
||||
Widget _buildSearchBar(ColorScheme colorScheme) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.white,
|
||||
color: colorScheme.surface,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withValues(alpha: 0.05),
|
||||
color: colorScheme.onSurface.withValues(alpha: 0.05),
|
||||
blurRadius: 4,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
@@ -176,20 +177,20 @@ class _OrdersPageState extends ConsumerState<OrdersPage> {
|
||||
controller: _searchController,
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Mã đơn hàng',
|
||||
hintStyle: const TextStyle(color: AppColors.grey500, fontSize: 14),
|
||||
prefixIcon: const Padding(
|
||||
padding: EdgeInsets.all(14),
|
||||
hintStyle: TextStyle(color: colorScheme.onSurfaceVariant, fontSize: 14),
|
||||
prefixIcon: Padding(
|
||||
padding: const EdgeInsets.all(14),
|
||||
child: FaIcon(
|
||||
FontAwesomeIcons.magnifyingGlass,
|
||||
color: AppColors.grey500,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
size: 18,
|
||||
),
|
||||
),
|
||||
suffixIcon: _searchController.text.isNotEmpty
|
||||
? IconButton(
|
||||
icon: const FaIcon(
|
||||
icon: FaIcon(
|
||||
FontAwesomeIcons.xmark,
|
||||
color: AppColors.grey500,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
size: 18,
|
||||
),
|
||||
onPressed: () {
|
||||
@@ -211,7 +212,7 @@ class _OrdersPageState extends ConsumerState<OrdersPage> {
|
||||
}
|
||||
|
||||
/// Build filter pills (dynamically from cached status list)
|
||||
Widget _buildFilterPills(String? selectedStatus) {
|
||||
Widget _buildFilterPills(String? selectedStatus, ColorScheme colorScheme) {
|
||||
final statusListAsync = ref.watch(orderStatusListProvider);
|
||||
|
||||
return SizedBox(
|
||||
@@ -229,6 +230,7 @@ class _OrdersPageState extends ConsumerState<OrdersPage> {
|
||||
onTap: () {
|
||||
ref.read(selectedOrderStatusProvider.notifier).clearSelection();
|
||||
},
|
||||
colorScheme: colorScheme,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
|
||||
@@ -244,6 +246,7 @@ class _OrdersPageState extends ConsumerState<OrdersPage> {
|
||||
.read(selectedOrderStatusProvider.notifier)
|
||||
.selectStatus(status.label);
|
||||
},
|
||||
colorScheme: colorScheme,
|
||||
),
|
||||
);
|
||||
}),
|
||||
@@ -260,6 +263,7 @@ class _OrdersPageState extends ConsumerState<OrdersPage> {
|
||||
label: 'Tất cả',
|
||||
isSelected: true,
|
||||
onTap: () {},
|
||||
colorScheme: colorScheme,
|
||||
),
|
||||
],
|
||||
);
|
||||
@@ -274,6 +278,7 @@ class _OrdersPageState extends ConsumerState<OrdersPage> {
|
||||
label: 'Tất cả',
|
||||
isSelected: true,
|
||||
onTap: () {},
|
||||
colorScheme: colorScheme,
|
||||
),
|
||||
],
|
||||
);
|
||||
@@ -287,13 +292,14 @@ class _OrdersPageState extends ConsumerState<OrdersPage> {
|
||||
required String label,
|
||||
required bool isSelected,
|
||||
required VoidCallback onTap,
|
||||
required ColorScheme colorScheme,
|
||||
}) {
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: isSelected ? AppColors.primaryBlue : AppColors.grey100,
|
||||
color: isSelected ? colorScheme.primary : colorScheme.surfaceContainerHighest,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: Center(
|
||||
@@ -302,7 +308,7 @@ class _OrdersPageState extends ConsumerState<OrdersPage> {
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: isSelected ? FontWeight.w600 : FontWeight.w400,
|
||||
color: isSelected ? Colors.white : AppColors.grey900,
|
||||
color: isSelected ? colorScheme.onPrimary : colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -311,7 +317,7 @@ class _OrdersPageState extends ConsumerState<OrdersPage> {
|
||||
}
|
||||
|
||||
/// Build empty state
|
||||
Widget _buildEmptyState() {
|
||||
Widget _buildEmptyState(ColorScheme colorScheme) {
|
||||
return SliverFillRemaining(
|
||||
child: Center(
|
||||
child: Column(
|
||||
@@ -320,21 +326,21 @@ class _OrdersPageState extends ConsumerState<OrdersPage> {
|
||||
FaIcon(
|
||||
FontAwesomeIcons.receipt,
|
||||
size: 80,
|
||||
color: AppColors.grey500.withValues(alpha: 0.5),
|
||||
color: colorScheme.onSurfaceVariant.withValues(alpha: 0.5),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
const Text(
|
||||
Text(
|
||||
'Không có đơn hàng nào',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.grey500,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
const Text(
|
||||
Text(
|
||||
'Thử tìm kiếm với từ khóa khác',
|
||||
style: TextStyle(fontSize: 14, color: AppColors.grey500),
|
||||
style: TextStyle(fontSize: 14, color: colorScheme.onSurfaceVariant),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -350,7 +356,7 @@ class _OrdersPageState extends ConsumerState<OrdersPage> {
|
||||
}
|
||||
|
||||
/// Build error state
|
||||
Widget _buildErrorState(Object error) {
|
||||
Widget _buildErrorState(Object error, ColorScheme colorScheme) {
|
||||
return SliverFillRemaining(
|
||||
child: Center(
|
||||
child: Column(
|
||||
@@ -362,18 +368,18 @@ class _OrdersPageState extends ConsumerState<OrdersPage> {
|
||||
color: AppColors.danger.withValues(alpha: 0.7),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
const Text(
|
||||
Text(
|
||||
'Có lỗi xảy ra',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.grey900,
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
error.toString(),
|
||||
style: const TextStyle(fontSize: 14, color: AppColors.grey500),
|
||||
style: TextStyle(fontSize: 14, color: colorScheme.onSurfaceVariant),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
|
||||
@@ -30,26 +30,27 @@ class PaymentDetailPage extends ConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
final invoicesAsync = ref.watch(invoicesProvider);
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: const Color(0xFFF4F6F8),
|
||||
backgroundColor: colorScheme.surfaceContainerLowest,
|
||||
appBar: AppBar(
|
||||
leading: IconButton(
|
||||
icon: const FaIcon(FontAwesomeIcons.arrowLeft, color: Colors.black, size: 20),
|
||||
icon: FaIcon(FontAwesomeIcons.arrowLeft, color: colorScheme.onSurface, size: 20),
|
||||
onPressed: () => context.pop(),
|
||||
),
|
||||
title: const Text(
|
||||
title: Text(
|
||||
'Chi tiết Hóa đơn',
|
||||
style: TextStyle(color: Colors.black),
|
||||
style: TextStyle(color: colorScheme.onSurface),
|
||||
),
|
||||
elevation: AppBarSpecs.elevation,
|
||||
backgroundColor: AppColors.white,
|
||||
foregroundColor: AppColors.grey900,
|
||||
backgroundColor: colorScheme.surface,
|
||||
foregroundColor: colorScheme.onSurface,
|
||||
centerTitle: false,
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const FaIcon(FontAwesomeIcons.shareNodes, color: Colors.black, size: 20),
|
||||
icon: FaIcon(FontAwesomeIcons.shareNodes, color: colorScheme.onSurface, size: 20),
|
||||
onPressed: () {
|
||||
// TODO: Implement share functionality
|
||||
ScaffoldMessenger.of(
|
||||
@@ -75,6 +76,7 @@ class PaymentDetailPage extends ConsumerWidget {
|
||||
children: [
|
||||
// Invoice Header Card
|
||||
_buildInvoiceHeader(
|
||||
context,
|
||||
invoice.invoiceNumber,
|
||||
invoice.orderId,
|
||||
invoice.issueDate,
|
||||
@@ -87,6 +89,7 @@ class PaymentDetailPage extends ConsumerWidget {
|
||||
|
||||
// Dates and Customer Info Card
|
||||
_buildCustomerInfo(
|
||||
context,
|
||||
invoice.issueDate,
|
||||
invoice.dueDate,
|
||||
invoice.isOverdue,
|
||||
@@ -94,18 +97,19 @@ class PaymentDetailPage extends ConsumerWidget {
|
||||
|
||||
|
||||
// Product List Card
|
||||
_buildProductList(),
|
||||
_buildProductList(context),
|
||||
|
||||
|
||||
// Payment History Card
|
||||
_buildPaymentHistory(
|
||||
context,
|
||||
invoice.amountPaid,
|
||||
invoice.issueDate
|
||||
),
|
||||
|
||||
|
||||
// Download Section Card
|
||||
_buildDownloadSection(invoice.invoiceNumber),
|
||||
_buildDownloadSection(context, invoice.invoiceNumber),
|
||||
|
||||
|
||||
// Support Button
|
||||
@@ -126,11 +130,11 @@ class PaymentDetailPage extends ConsumerWidget {
|
||||
vertical: 16,
|
||||
horizontal: 16,
|
||||
),
|
||||
side: const BorderSide(
|
||||
color: AppColors.grey100,
|
||||
side: BorderSide(
|
||||
color: colorScheme.outlineVariant,
|
||||
width: 2,
|
||||
),
|
||||
foregroundColor: AppColors.grey900,
|
||||
foregroundColor: colorScheme.onSurface,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
@@ -172,10 +176,10 @@ class PaymentDetailPage extends ConsumerWidget {
|
||||
(invoice.status == InvoiceStatus.paid ||
|
||||
invoice.isPaid)
|
||||
? AppColors.success
|
||||
: AppColors.primaryBlue,
|
||||
: colorScheme.primary,
|
||||
disabledBackgroundColor: AppColors.success,
|
||||
foregroundColor: Colors.white,
|
||||
disabledForegroundColor: Colors.white,
|
||||
foregroundColor: colorScheme.surface,
|
||||
disabledForegroundColor: colorScheme.surface,
|
||||
elevation: 0,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
@@ -201,9 +205,10 @@ class PaymentDetailPage extends ConsumerWidget {
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'Không tìm thấy hóa đơn',
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
@@ -220,6 +225,7 @@ class PaymentDetailPage extends ConsumerWidget {
|
||||
|
||||
/// Build invoice header section
|
||||
Widget _buildInvoiceHeader(
|
||||
BuildContext context,
|
||||
String invoiceNumber,
|
||||
String? orderId,
|
||||
DateTime issueDate,
|
||||
@@ -228,6 +234,8 @@ class PaymentDetailPage extends ConsumerWidget {
|
||||
double amountPaid,
|
||||
double amountRemaining,
|
||||
) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return Card(
|
||||
elevation: 1,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
@@ -240,19 +248,19 @@ class PaymentDetailPage extends ConsumerWidget {
|
||||
children: [
|
||||
Text(
|
||||
'#$invoiceNumber',
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: AppColors.grey900,
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
if (orderId != null)
|
||||
Text(
|
||||
'Đơn hàng: #$orderId | Ngày đặt: ${_formatDate(issueDate)}',
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: AppColors.grey500,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
@@ -269,17 +277,19 @@ class PaymentDetailPage extends ConsumerWidget {
|
||||
Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.grey50,
|
||||
color: colorScheme.surfaceContainerLowest,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
_buildSummaryRow(
|
||||
context,
|
||||
'Tổng tiền hóa đơn:',
|
||||
totalAmount.toVNCurrency,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_buildSummaryRow(
|
||||
context,
|
||||
'Đã thanh toán:',
|
||||
amountPaid.toVNCurrency,
|
||||
),
|
||||
@@ -288,6 +298,7 @@ class PaymentDetailPage extends ConsumerWidget {
|
||||
child: Divider(height: 2, thickness: 2),
|
||||
),
|
||||
_buildSummaryRow(
|
||||
context,
|
||||
'Còn lại:',
|
||||
amountRemaining.toVNCurrency,
|
||||
isHighlighted: true,
|
||||
@@ -306,10 +317,13 @@ class PaymentDetailPage extends ConsumerWidget {
|
||||
|
||||
/// Build customer info and dates section
|
||||
Widget _buildCustomerInfo(
|
||||
BuildContext context,
|
||||
DateTime issueDate,
|
||||
DateTime dueDate,
|
||||
bool isOverdue,
|
||||
) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return Card(
|
||||
elevation: 1,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
@@ -324,20 +338,20 @@ class PaymentDetailPage extends ConsumerWidget {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
Text(
|
||||
'Ngày đặt hàng',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: AppColors.grey500,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
_formatDate(issueDate),
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.grey900,
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -347,11 +361,11 @@ class PaymentDetailPage extends ConsumerWidget {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
Text(
|
||||
'Hạn thanh toán',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: AppColors.grey500,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
@@ -362,7 +376,7 @@ class PaymentDetailPage extends ConsumerWidget {
|
||||
fontWeight: FontWeight.w600,
|
||||
color: isOverdue
|
||||
? AppColors.danger
|
||||
: AppColors.grey900,
|
||||
: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -377,28 +391,28 @@ class PaymentDetailPage extends ConsumerWidget {
|
||||
Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.grey50,
|
||||
color: colorScheme.surfaceContainerLowest,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
Text(
|
||||
'Thông tin khách hàng',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.grey900,
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
const Text(
|
||||
Text(
|
||||
'Công ty TNHH Xây Dựng Minh An\n'
|
||||
'Địa chỉ: 123 Nguyễn Văn Linh, Quận 7, TP.HCM\n'
|
||||
'SĐT: 0901234567 | Email: contact@minhan.com',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: AppColors.grey500,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
height: 1.5,
|
||||
),
|
||||
),
|
||||
@@ -412,7 +426,9 @@ class PaymentDetailPage extends ConsumerWidget {
|
||||
}
|
||||
|
||||
/// Build product list section
|
||||
Widget _buildProductList() {
|
||||
Widget _buildProductList(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
// Mock product data - in real app, this would come from order items
|
||||
final products = [
|
||||
{
|
||||
@@ -445,14 +461,14 @@ class PaymentDetailPage extends ConsumerWidget {
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
FaIcon(FontAwesomeIcons.box, color: AppColors.primaryBlue, size: 18),
|
||||
FaIcon(FontAwesomeIcons.box, color: colorScheme.primary, size: 18),
|
||||
const SizedBox(width: 8),
|
||||
const Text(
|
||||
Text(
|
||||
'Danh sách sản phẩm',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: AppColors.grey900,
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -465,7 +481,7 @@ class PaymentDetailPage extends ConsumerWidget {
|
||||
margin: const EdgeInsets.only(bottom: 12),
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: AppColors.grey100),
|
||||
border: Border.all(color: colorScheme.outlineVariant),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Row(
|
||||
@@ -476,12 +492,12 @@ class PaymentDetailPage extends ConsumerWidget {
|
||||
width: 60,
|
||||
height: 60,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.grey50,
|
||||
color: colorScheme.surfaceContainerLowest,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: const FaIcon(
|
||||
child: FaIcon(
|
||||
FontAwesomeIcons.image,
|
||||
color: AppColors.grey500,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
size: 22,
|
||||
),
|
||||
),
|
||||
@@ -493,18 +509,18 @@ class PaymentDetailPage extends ConsumerWidget {
|
||||
children: [
|
||||
Text(
|
||||
product['name']!,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.grey900,
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
'SKU: ${product['sku']}',
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: AppColors.grey500,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
@@ -514,17 +530,17 @@ class PaymentDetailPage extends ConsumerWidget {
|
||||
children: [
|
||||
Text(
|
||||
'Số lượng: ${product['quantity']}',
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: AppColors.grey500,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
product['price']!,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.grey900,
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -545,9 +561,11 @@ class PaymentDetailPage extends ConsumerWidget {
|
||||
|
||||
/// Build payment history section
|
||||
Widget _buildPaymentHistory(
|
||||
BuildContext context,
|
||||
double amountPaid,
|
||||
DateTime paymentDate,
|
||||
) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
final hasHistory = amountPaid > 0;
|
||||
|
||||
return Card(
|
||||
@@ -558,16 +576,16 @@ class PaymentDetailPage extends ConsumerWidget {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Row(
|
||||
Row(
|
||||
children: [
|
||||
FaIcon(FontAwesomeIcons.clockRotateLeft, color: AppColors.primaryBlue, size: 18),
|
||||
SizedBox(width: 8),
|
||||
FaIcon(FontAwesomeIcons.clockRotateLeft, color: colorScheme.primary, size: 18),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'Lịch sử thanh toán',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: AppColors.grey900,
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -578,7 +596,7 @@ class PaymentDetailPage extends ConsumerWidget {
|
||||
Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: AppColors.grey100),
|
||||
border: Border.all(color: colorScheme.outlineVariant),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Row(
|
||||
@@ -603,28 +621,28 @@ class PaymentDetailPage extends ConsumerWidget {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
Text(
|
||||
'Thanh toán lần 1',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.grey900,
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
const Text(
|
||||
Text(
|
||||
'Chuyển khoản | Ref: TK20241020001',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: AppColors.grey500,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
'${_formatDate(paymentDate)} - 14:30',
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: AppColors.grey500,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -650,23 +668,23 @@ class PaymentDetailPage extends ConsumerWidget {
|
||||
FaIcon(
|
||||
FontAwesomeIcons.receipt,
|
||||
size: 48,
|
||||
color: AppColors.grey100,
|
||||
color: colorScheme.outlineVariant,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
const Text(
|
||||
Text(
|
||||
'Chưa có lịch sử thanh toán',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.grey500,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
const Text(
|
||||
Text(
|
||||
'Hóa đơn này chưa được thanh toán',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: AppColors.grey500,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -680,7 +698,9 @@ class PaymentDetailPage extends ConsumerWidget {
|
||||
}
|
||||
|
||||
/// Build download section
|
||||
Widget _buildDownloadSection(String invoiceNumber) {
|
||||
Widget _buildDownloadSection(BuildContext context, String invoiceNumber) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return Card(
|
||||
elevation: 1,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
@@ -689,22 +709,23 @@ class PaymentDetailPage extends ConsumerWidget {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Row(
|
||||
Row(
|
||||
children: [
|
||||
FaIcon(FontAwesomeIcons.download, color: AppColors.primaryBlue, size: 18),
|
||||
SizedBox(width: 8),
|
||||
FaIcon(FontAwesomeIcons.download, color: colorScheme.primary, size: 18),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'Tải chứng từ',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: AppColors.grey900,
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_buildDownloadButton(
|
||||
context,
|
||||
icon: FontAwesomeIcons.filePdf,
|
||||
label: 'Hóa đơn PDF',
|
||||
onTap: () {
|
||||
@@ -713,6 +734,7 @@ class PaymentDetailPage extends ConsumerWidget {
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_buildDownloadButton(
|
||||
context,
|
||||
icon: FontAwesomeIcons.receipt,
|
||||
label: 'Phiếu thu PDF',
|
||||
onTap: () {
|
||||
@@ -726,33 +748,36 @@ class PaymentDetailPage extends ConsumerWidget {
|
||||
}
|
||||
|
||||
/// Build download button
|
||||
Widget _buildDownloadButton({
|
||||
Widget _buildDownloadButton(
|
||||
BuildContext context, {
|
||||
required IconData icon,
|
||||
required String label,
|
||||
required VoidCallback onTap,
|
||||
}) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.grey50,
|
||||
border: Border.all(color: AppColors.grey100),
|
||||
color: colorScheme.surfaceContainerLowest,
|
||||
border: Border.all(color: colorScheme.outlineVariant),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
FaIcon(icon, size: 18, color: AppColors.grey500),
|
||||
FaIcon(icon, size: 18, color: colorScheme.onSurfaceVariant),
|
||||
const SizedBox(width: 8),
|
||||
Flexible(
|
||||
child: Text(
|
||||
label,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.grey500,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
@@ -788,11 +813,14 @@ class PaymentDetailPage extends ConsumerWidget {
|
||||
|
||||
/// Build summary row
|
||||
Widget _buildSummaryRow(
|
||||
BuildContext context,
|
||||
String label,
|
||||
String value, {
|
||||
bool isHighlighted = false,
|
||||
Color? valueColor,
|
||||
}) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
@@ -801,7 +829,7 @@ class PaymentDetailPage extends ConsumerWidget {
|
||||
style: TextStyle(
|
||||
fontSize: isHighlighted ? 18 : 16,
|
||||
fontWeight: isHighlighted ? FontWeight.w700 : FontWeight.w400,
|
||||
color: AppColors.grey500,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
@@ -809,7 +837,7 @@ class PaymentDetailPage extends ConsumerWidget {
|
||||
style: TextStyle(
|
||||
fontSize: isHighlighted ? 18 : 16,
|
||||
fontWeight: isHighlighted ? FontWeight.w700 : FontWeight.w600,
|
||||
color: valueColor ?? AppColors.grey900,
|
||||
color: valueColor ?? colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -822,7 +850,7 @@ class PaymentDetailPage extends ConsumerWidget {
|
||||
case InvoiceStatus.draft:
|
||||
return AppColors.grey500;
|
||||
case InvoiceStatus.issued:
|
||||
return const Color(0xFFF59E0B);
|
||||
return AppColors.warning;
|
||||
case InvoiceStatus.partiallyPaid:
|
||||
return AppColors.info;
|
||||
case InvoiceStatus.paid:
|
||||
@@ -832,7 +860,7 @@ class PaymentDetailPage extends ConsumerWidget {
|
||||
case InvoiceStatus.cancelled:
|
||||
return AppColors.grey500;
|
||||
case InvoiceStatus.refunded:
|
||||
return const Color(0xFFF97316);
|
||||
return AppColors.warning;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,8 @@ class PaymentQrPage extends HookConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
// QR code data state
|
||||
final qrCodeData = useState<Map<String, dynamic>?>(null);
|
||||
final isLoadingQr = useState<bool>(true);
|
||||
@@ -93,18 +95,18 @@ class PaymentQrPage extends HookConsumerWidget {
|
||||
'${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}';
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: const Color(0xFFF4F6F8),
|
||||
backgroundColor: colorScheme.surfaceContainerLowest,
|
||||
appBar: AppBar(
|
||||
backgroundColor: Colors.white,
|
||||
backgroundColor: colorScheme.surface,
|
||||
elevation: 0,
|
||||
leading: IconButton(
|
||||
icon: const FaIcon(FontAwesomeIcons.arrowLeft, color: Colors.black, size: 20),
|
||||
icon: FaIcon(FontAwesomeIcons.arrowLeft, color: colorScheme.onSurface, size: 20),
|
||||
onPressed: () => context.pop(),
|
||||
),
|
||||
title: const Text(
|
||||
title: Text(
|
||||
'Thanh toán',
|
||||
style: TextStyle(
|
||||
color: Colors.black,
|
||||
color: colorScheme.onSurface,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
@@ -112,7 +114,7 @@ class PaymentQrPage extends HookConsumerWidget {
|
||||
centerTitle: false,
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const FaIcon(FontAwesomeIcons.circleInfo, color: Colors.black, size: 20),
|
||||
icon: FaIcon(FontAwesomeIcons.circleInfo, color: colorScheme.onSurface, size: 20),
|
||||
onPressed: () => _showInfoDialog(context),
|
||||
),
|
||||
const SizedBox(width: AppSpacing.sm),
|
||||
@@ -125,6 +127,7 @@ class PaymentQrPage extends HookConsumerWidget {
|
||||
|
||||
// QR Code Card
|
||||
_buildQrCodeCard(
|
||||
context,
|
||||
orderId,
|
||||
qrCodeData.value?['qr_code'] as String?,
|
||||
isLoadingQr.value,
|
||||
@@ -169,7 +172,7 @@ class PaymentQrPage extends HookConsumerWidget {
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
|
||||
// Timer
|
||||
_buildTimer(timerDisplay, remainingSeconds.value),
|
||||
_buildTimer(context, timerDisplay, remainingSeconds.value),
|
||||
|
||||
const SizedBox(height: AppSpacing.lg),
|
||||
],
|
||||
@@ -180,19 +183,22 @@ class PaymentQrPage extends HookConsumerWidget {
|
||||
|
||||
/// Build QR code card
|
||||
Widget _buildQrCodeCard(
|
||||
BuildContext context,
|
||||
String orderId,
|
||||
String? qrCodeData,
|
||||
bool isLoading,
|
||||
) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
|
||||
padding: const EdgeInsets.all(AppSpacing.md),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
color: colorScheme.surface,
|
||||
borderRadius: BorderRadius.circular(AppRadius.card),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withValues(alpha: 0.05),
|
||||
color: colorScheme.onSurface.withValues(alpha: 0.05),
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
@@ -200,12 +206,12 @@ class PaymentQrPage extends HookConsumerWidget {
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
const Text(
|
||||
Text(
|
||||
'Quét mã QR để thanh toán',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Color(0xFF212121),
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
@@ -214,9 +220,9 @@ class PaymentQrPage extends HookConsumerWidget {
|
||||
height: 220,
|
||||
padding: const EdgeInsets.all(10),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
color: colorScheme.surface,
|
||||
borderRadius: BorderRadius.circular(AppRadius.card),
|
||||
border: Border.all(color: const Color(0xFFE2E8F0)),
|
||||
border: Border.all(color: colorScheme.outlineVariant),
|
||||
),
|
||||
child: isLoading
|
||||
? const Center(
|
||||
@@ -227,26 +233,26 @@ class PaymentQrPage extends HookConsumerWidget {
|
||||
data: qrCodeData,
|
||||
version: QrVersions.auto,
|
||||
size: 200.0,
|
||||
backgroundColor: Colors.white,
|
||||
backgroundColor: colorScheme.surface,
|
||||
errorCorrectionLevel: QrErrorCorrectLevel.M,
|
||||
)
|
||||
: const Column(
|
||||
: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
FaIcon(FontAwesomeIcons.qrcode, size: 80, color: AppColors.grey500),
|
||||
SizedBox(height: 8),
|
||||
FaIcon(FontAwesomeIcons.qrcode, size: 80, color: colorScheme.onSurfaceVariant),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'Không thể tải mã QR',
|
||||
style: TextStyle(fontSize: 12, color: AppColors.grey500),
|
||||
style: TextStyle(fontSize: 12, color: colorScheme.onSurfaceVariant),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
const Text(
|
||||
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),
|
||||
style: TextStyle(fontSize: 14, color: colorScheme.onSurfaceVariant),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -262,15 +268,17 @@ class PaymentQrPage extends HookConsumerWidget {
|
||||
String? accountName,
|
||||
bool isLoading,
|
||||
) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
|
||||
padding: const EdgeInsets.all(AppSpacing.md),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
color: colorScheme.surface,
|
||||
borderRadius: BorderRadius.circular(AppRadius.card),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withValues(alpha: 0.05),
|
||||
color: colorScheme.onSurface.withValues(alpha: 0.05),
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
@@ -279,12 +287,12 @@ class PaymentQrPage extends HookConsumerWidget {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
Text(
|
||||
'Thông tin chuyển khoản',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Color(0xFF212121),
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
@@ -329,25 +337,25 @@ class PaymentQrPage extends HookConsumerWidget {
|
||||
Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFE3F2FD),
|
||||
color: colorScheme.primary.withValues(alpha: 0.1),
|
||||
borderRadius: BorderRadius.circular(AppRadius.card),
|
||||
border: Border.all(color: const Color(0xFF90CAF9)),
|
||||
border: Border.all(color: colorScheme.primary.withValues(alpha: 0.3)),
|
||||
),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const FaIcon(
|
||||
FaIcon(
|
||||
FontAwesomeIcons.lightbulb,
|
||||
color: AppColors.primaryBlue,
|
||||
color: colorScheme.primary,
|
||||
size: 18,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: RichText(
|
||||
text: TextSpan(
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: Color(0xFF1565C0),
|
||||
color: colorScheme.primary,
|
||||
height: 1.4,
|
||||
),
|
||||
children: [
|
||||
@@ -377,28 +385,30 @@ class PaymentQrPage extends HookConsumerWidget {
|
||||
required String label,
|
||||
required String value,
|
||||
}) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return Row(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Text(
|
||||
label,
|
||||
style: const TextStyle(fontSize: 14, color: AppColors.grey500),
|
||||
style: TextStyle(fontSize: 14, color: colorScheme.onSurfaceVariant),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 3,
|
||||
child: Text(
|
||||
value,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF212121),
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
icon: const FaIcon(FontAwesomeIcons.copy, size: 18, color: AppColors.primaryBlue),
|
||||
icon: FaIcon(FontAwesomeIcons.copy, size: 18, color: colorScheme.primary),
|
||||
onPressed: () => _copyToClipboard(context, value),
|
||||
padding: EdgeInsets.zero,
|
||||
constraints: const BoxConstraints(),
|
||||
@@ -413,15 +423,17 @@ class PaymentQrPage extends HookConsumerWidget {
|
||||
String? imagePath,
|
||||
VoidCallback onSelectImage,
|
||||
) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
|
||||
padding: const EdgeInsets.all(AppSpacing.md),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
color: colorScheme.surface,
|
||||
borderRadius: BorderRadius.circular(AppRadius.card),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withValues(alpha: 0.05),
|
||||
color: colorScheme.onSurface.withValues(alpha: 0.05),
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
@@ -430,12 +442,12 @@ class PaymentQrPage extends HookConsumerWidget {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
Text(
|
||||
'Ảnh hóa đơn',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Color(0xFF212121),
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
@@ -448,10 +460,10 @@ class PaymentQrPage extends HookConsumerWidget {
|
||||
width: double.infinity,
|
||||
height: 200,
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFF4F6F8),
|
||||
color: colorScheme.surfaceContainerLowest,
|
||||
borderRadius: BorderRadius.circular(AppRadius.card),
|
||||
border: Border.all(
|
||||
color: const Color(0xFFE2E8F0),
|
||||
color: colorScheme.outlineVariant,
|
||||
width: 2,
|
||||
style: BorderStyle.solid,
|
||||
),
|
||||
@@ -472,7 +484,7 @@ class PaymentQrPage extends HookConsumerWidget {
|
||||
right: 8,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black.withValues(alpha: 0.6),
|
||||
color: colorScheme.onSurface.withValues(alpha: 0.6),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
@@ -481,17 +493,17 @@ class PaymentQrPage extends HookConsumerWidget {
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: const [
|
||||
children: [
|
||||
FaIcon(
|
||||
FontAwesomeIcons.pen,
|
||||
color: Colors.white,
|
||||
color: colorScheme.surface,
|
||||
size: 12,
|
||||
),
|
||||
SizedBox(width: 6),
|
||||
const SizedBox(width: 6),
|
||||
Text(
|
||||
'Đổi ảnh',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
color: colorScheme.surface,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
@@ -510,30 +522,30 @@ class PaymentQrPage extends HookConsumerWidget {
|
||||
width: 60,
|
||||
height: 60,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.primaryBlue.withValues(alpha: 0.1),
|
||||
color: colorScheme.primary.withValues(alpha: 0.1),
|
||||
borderRadius: BorderRadius.circular(30),
|
||||
),
|
||||
child: const Icon(
|
||||
child: Icon(
|
||||
FontAwesomeIcons.image,
|
||||
color: AppColors.primaryBlue,
|
||||
color: colorScheme.primary,
|
||||
size: 24,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
const Text(
|
||||
Text(
|
||||
'Chạm để chọn ảnh hóa đơn',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: AppColors.grey500,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
const Text(
|
||||
Text(
|
||||
'Hỗ trợ: JPG, PNG',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: AppColors.grey500,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -553,6 +565,8 @@ class PaymentQrPage extends HookConsumerWidget {
|
||||
bool hasImage,
|
||||
VoidCallback onUpload,
|
||||
) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
|
||||
child: Column(
|
||||
@@ -563,12 +577,12 @@ class PaymentQrPage extends HookConsumerWidget {
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: (isUploading || !hasImage) ? null : onUpload,
|
||||
icon: isUploading
|
||||
? const SizedBox(
|
||||
? SizedBox(
|
||||
width: 18,
|
||||
height: 18,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
|
||||
valueColor: AlwaysStoppedAnimation<Color>(colorScheme.surface),
|
||||
),
|
||||
)
|
||||
: const FaIcon(FontAwesomeIcons.camera, size: 18),
|
||||
@@ -577,15 +591,15 @@ class PaymentQrPage extends HookConsumerWidget {
|
||||
style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w600),
|
||||
),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColors.primaryBlue,
|
||||
foregroundColor: Colors.white,
|
||||
backgroundColor: colorScheme.primary,
|
||||
foregroundColor: colorScheme.onPrimary,
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
elevation: 0,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(AppRadius.button),
|
||||
),
|
||||
disabledBackgroundColor: AppColors.grey100,
|
||||
disabledForegroundColor: AppColors.grey500,
|
||||
disabledBackgroundColor: colorScheme.surfaceContainerLowest,
|
||||
disabledForegroundColor: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -602,9 +616,9 @@ class PaymentQrPage extends HookConsumerWidget {
|
||||
style: TextStyle(fontSize: 15, fontWeight: FontWeight.w600),
|
||||
),
|
||||
style: OutlinedButton.styleFrom(
|
||||
foregroundColor: AppColors.grey900,
|
||||
side: const BorderSide(
|
||||
color: AppColors.grey100,
|
||||
foregroundColor: colorScheme.onSurface,
|
||||
side: BorderSide(
|
||||
color: colorScheme.outlineVariant,
|
||||
width: 1.5,
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
@@ -620,22 +634,24 @@ class PaymentQrPage extends HookConsumerWidget {
|
||||
}
|
||||
|
||||
/// Build countdown timer
|
||||
Widget _buildTimer(String timerDisplay, int remainingSeconds) {
|
||||
Widget _buildTimer(BuildContext context, String timerDisplay, int remainingSeconds) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
color: colorScheme.surface,
|
||||
borderRadius: BorderRadius.circular(AppRadius.card),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const FaIcon(FontAwesomeIcons.clock, size: 16, color: AppColors.grey500),
|
||||
FaIcon(FontAwesomeIcons.clock, size: 16, color: colorScheme.onSurfaceVariant),
|
||||
const SizedBox(width: 8),
|
||||
const Text(
|
||||
Text(
|
||||
'Thời gian thanh toán: ',
|
||||
style: TextStyle(fontSize: 14, color: AppColors.grey500),
|
||||
style: TextStyle(fontSize: 14, color: colorScheme.onSurfaceVariant),
|
||||
),
|
||||
Text(
|
||||
timerDisplay,
|
||||
@@ -644,7 +660,7 @@ class PaymentQrPage extends HookConsumerWidget {
|
||||
fontWeight: FontWeight.bold,
|
||||
color: remainingSeconds < 300
|
||||
? AppColors.danger
|
||||
: AppColors.grey900,
|
||||
: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -715,6 +731,8 @@ class PaymentQrPage extends HookConsumerWidget {
|
||||
BuildContext context,
|
||||
ValueNotifier<String?> selectedImagePath,
|
||||
) async {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
// Show bottom sheet to select camera or gallery
|
||||
final ImageSource? source = await showModalBottomSheet<ImageSource>(
|
||||
context: context,
|
||||
@@ -726,27 +744,27 @@ class PaymentQrPage extends HookConsumerWidget {
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Text(
|
||||
Text(
|
||||
'Chọn ảnh hóa đơn',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Color(0xFF212121),
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
ListTile(
|
||||
leading: const FaIcon(
|
||||
leading: FaIcon(
|
||||
FontAwesomeIcons.camera,
|
||||
color: AppColors.primaryBlue,
|
||||
color: colorScheme.primary,
|
||||
),
|
||||
title: const Text('Chụp ảnh'),
|
||||
onTap: () => Navigator.of(context).pop(ImageSource.camera),
|
||||
),
|
||||
ListTile(
|
||||
leading: const FaIcon(
|
||||
leading: FaIcon(
|
||||
FontAwesomeIcons.image,
|
||||
color: AppColors.primaryBlue,
|
||||
color: colorScheme.primary,
|
||||
),
|
||||
title: const Text('Chọn từ thư viện'),
|
||||
onTap: () => Navigator.of(context).pop(ImageSource.gallery),
|
||||
@@ -811,6 +829,8 @@ class PaymentQrPage extends HookConsumerWidget {
|
||||
ValueNotifier<String?> selectedImagePath,
|
||||
ValueNotifier<bool> isUploadingBill,
|
||||
) async {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
if (selectedImagePath.value == null) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
@@ -839,7 +859,7 @@ class PaymentQrPage extends HookConsumerWidget {
|
||||
ElevatedButton(
|
||||
onPressed: () => Navigator.of(context).pop(true),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColors.primaryBlue,
|
||||
backgroundColor: colorScheme.primary,
|
||||
),
|
||||
child: const Text('Upload'),
|
||||
),
|
||||
|
||||
@@ -26,7 +26,7 @@ class PaymentsPage extends ConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final paymentsAsync = ref.watch(paymentsProvider);
|
||||
final colorScheme = context.colorScheme;
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: colorScheme.surfaceContainerLowest,
|
||||
@@ -79,7 +79,7 @@ class PaymentsPage extends ConsumerWidget {
|
||||
|
||||
/// Build error state
|
||||
Widget _buildErrorState(BuildContext context, WidgetRef ref, Object error) {
|
||||
final colorScheme = context.colorScheme;
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
@@ -116,7 +116,7 @@ class PaymentsPage extends ConsumerWidget {
|
||||
|
||||
/// Build empty state
|
||||
Widget _buildEmptyState(BuildContext context, WidgetRef ref) {
|
||||
final colorScheme = context.colorScheme;
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
return RefreshIndicator(
|
||||
onRefresh: () async {
|
||||
await ref.read(paymentsProvider.notifier).refresh();
|
||||
@@ -164,7 +164,7 @@ class PaymentsPage extends ConsumerWidget {
|
||||
|
||||
/// Show transaction detail modal
|
||||
void _showTransactionDetail(BuildContext context, Payment payment) {
|
||||
final colorScheme = context.colorScheme;
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
final currencyFormatter = NumberFormat.currency(
|
||||
locale: 'vi_VN',
|
||||
symbol: 'đ',
|
||||
@@ -302,8 +302,8 @@ class _TransactionCard extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
final dateFormatter = DateFormat('dd/MM/yyyy');
|
||||
final colorScheme = context.colorScheme;
|
||||
|
||||
return Card(
|
||||
margin: const EdgeInsets.only(bottom: 12),
|
||||
@@ -416,7 +416,7 @@ class _DetailRow extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = context.colorScheme;
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
decoration: BoxDecoration(
|
||||
|
||||
@@ -32,6 +32,8 @@ class InvoiceCard extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
final currencyFormatter = NumberFormat.currency(
|
||||
locale: 'vi_VN',
|
||||
symbol: 'đ',
|
||||
@@ -61,10 +63,10 @@ class InvoiceCard extends StatelessWidget {
|
||||
// Invoice number
|
||||
Text(
|
||||
'Mã hoá đơn #${invoice.invoiceNumber}',
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: AppColors.grey900,
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
@@ -72,9 +74,9 @@ class InvoiceCard extends StatelessWidget {
|
||||
if (invoice.orderId != null)
|
||||
Text(
|
||||
'Đơn hàng: #${invoice.orderId}',
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: AppColors.grey500,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -88,10 +90,18 @@ class InvoiceCard extends StatelessWidget {
|
||||
const SizedBox(height: 12),
|
||||
|
||||
// Invoice dates
|
||||
_buildDetailRow('Ngày hóa đơn:', _formatDate(invoice.issueDate)),
|
||||
_buildDetailRow(
|
||||
context,
|
||||
'Ngày hóa đơn:',
|
||||
_formatDate(invoice.issueDate),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
|
||||
_buildDetailRow('Hạn thanh toán:', _formatDate(invoice.dueDate)),
|
||||
_buildDetailRow(
|
||||
context,
|
||||
'Hạn thanh toán:',
|
||||
_formatDate(invoice.dueDate),
|
||||
),
|
||||
|
||||
const SizedBox(height: 12),
|
||||
|
||||
@@ -99,13 +109,14 @@ class InvoiceCard extends StatelessWidget {
|
||||
Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.grey50,
|
||||
color: colorScheme.surfaceContainerHighest,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Column(
|
||||
spacing: 2,
|
||||
children: [
|
||||
_buildPaymentRow(
|
||||
context,
|
||||
'Tổng tiền:',
|
||||
currencyFormatter.format(invoice.totalAmount),
|
||||
fontWeight: FontWeight.w600,
|
||||
@@ -113,6 +124,7 @@ class InvoiceCard extends StatelessWidget {
|
||||
if (invoice.amountPaid > 0) ...[
|
||||
const SizedBox(height: 6),
|
||||
_buildPaymentRow(
|
||||
context,
|
||||
'Đã thanh toán:',
|
||||
currencyFormatter.format(invoice.amountPaid),
|
||||
valueColor: AppColors.success,
|
||||
@@ -122,6 +134,7 @@ class InvoiceCard extends StatelessWidget {
|
||||
if (invoice.amountRemaining > 0) ...[
|
||||
const SizedBox(height: 6),
|
||||
_buildPaymentRow(
|
||||
context,
|
||||
'Còn lại:',
|
||||
currencyFormatter.format(invoice.amountRemaining),
|
||||
valueColor: invoice.isOverdue
|
||||
@@ -137,7 +150,7 @@ class InvoiceCard extends StatelessWidget {
|
||||
const SizedBox(height: 12),
|
||||
|
||||
// Action button
|
||||
_buildActionButton(),
|
||||
_buildActionButton(context),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -146,7 +159,9 @@ class InvoiceCard extends StatelessWidget {
|
||||
}
|
||||
|
||||
/// Build detail row
|
||||
Widget _buildDetailRow(String label, String value) {
|
||||
Widget _buildDetailRow(BuildContext context, String label, String value) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
@@ -154,14 +169,14 @@ class InvoiceCard extends StatelessWidget {
|
||||
children: [
|
||||
Text(
|
||||
label,
|
||||
style: const TextStyle(fontSize: 14, color: AppColors.grey500),
|
||||
style: TextStyle(fontSize: 14, color: colorScheme.onSurfaceVariant),
|
||||
),
|
||||
|
||||
Text(
|
||||
value,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: AppColors.grey900,
|
||||
color: colorScheme.onSurface,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
@@ -171,24 +186,27 @@ class InvoiceCard extends StatelessWidget {
|
||||
|
||||
/// Build payment summary row
|
||||
Widget _buildPaymentRow(
|
||||
BuildContext context,
|
||||
String label,
|
||||
String value, {
|
||||
Color? valueColor,
|
||||
FontWeight? fontWeight,
|
||||
}) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
label,
|
||||
style: const TextStyle(fontSize: 13, color: AppColors.grey500),
|
||||
style: TextStyle(fontSize: 13, color: colorScheme.onSurfaceVariant),
|
||||
),
|
||||
Text(
|
||||
value,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: fontWeight ?? FontWeight.w400,
|
||||
color: valueColor ?? AppColors.grey900,
|
||||
color: valueColor ?? colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -219,10 +237,11 @@ class InvoiceCard extends StatelessWidget {
|
||||
}
|
||||
|
||||
/// Build action button
|
||||
Widget _buildActionButton() {
|
||||
Widget _buildActionButton(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
final isPaid = invoice.status == InvoiceStatus.paid || invoice.isPaid;
|
||||
final buttonText = isPaid ? 'Đã hoàn tất' : 'Thanh toán';
|
||||
final buttonColor = isPaid ? AppColors.success : AppColors.primaryBlue;
|
||||
final buttonColor = isPaid ? AppColors.success : colorScheme.primary;
|
||||
|
||||
return SizedBox(
|
||||
width: double.infinity,
|
||||
@@ -230,9 +249,9 @@ class InvoiceCard extends StatelessWidget {
|
||||
onPressed: isPaid ? null : onPaymentTap,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: buttonColor,
|
||||
disabledBackgroundColor: AppColors.grey100,
|
||||
foregroundColor: Colors.white,
|
||||
disabledForegroundColor: AppColors.grey500,
|
||||
disabledBackgroundColor: colorScheme.surfaceContainerHighest,
|
||||
foregroundColor: colorScheme.surface,
|
||||
disabledForegroundColor: colorScheme.onSurfaceVariant,
|
||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
||||
elevation: 0,
|
||||
|
||||
@@ -6,7 +6,6 @@ library;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:worker/core/enums/status_color.dart';
|
||||
import 'package:worker/core/theme/colors.dart';
|
||||
import 'package:worker/features/orders/domain/entities/order.dart';
|
||||
|
||||
/// Order Card Widget
|
||||
@@ -23,6 +22,7 @@ class OrderCard extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
final currencyFormatter = NumberFormat.currency(
|
||||
locale: 'vi_VN',
|
||||
symbol: 'đ',
|
||||
@@ -49,20 +49,20 @@ class OrderCard extends StatelessWidget {
|
||||
// Order number
|
||||
Text(
|
||||
'#${order.name}',
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: AppColors.grey900,
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
|
||||
// Amount
|
||||
Text(
|
||||
currencyFormatter.format(order.grandTotal),
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: AppColors.primaryBlue,
|
||||
color: colorScheme.primary,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -71,13 +71,21 @@ class OrderCard extends StatelessWidget {
|
||||
const SizedBox(height: 12),
|
||||
|
||||
// Order details
|
||||
_buildDetailRow('Ngày đặt:', _formatDate(order.transactionDate)),
|
||||
_buildDetailRow(
|
||||
context,
|
||||
'Ngày đặt:',
|
||||
_formatDate(order.transactionDate),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
|
||||
_buildDetailRow('Ngày giao:', _formatDate(order.deliveryDate)),
|
||||
_buildDetailRow(
|
||||
context,
|
||||
'Ngày giao:',
|
||||
_formatDate(order.deliveryDate),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
|
||||
_buildDetailRow('Địa chỉ:', order.address),
|
||||
_buildDetailRow(context, 'Địa chỉ:', order.address),
|
||||
const SizedBox(height: 12),
|
||||
|
||||
// Status badge
|
||||
@@ -90,19 +98,27 @@ class OrderCard extends StatelessWidget {
|
||||
}
|
||||
|
||||
/// Build detail row
|
||||
Widget _buildDetailRow(String label, String value) {
|
||||
Widget _buildDetailRow(BuildContext context, String label, String value) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
label,
|
||||
style: const TextStyle(fontSize: 14, color: AppColors.grey500),
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(
|
||||
value,
|
||||
style: const TextStyle(fontSize: 14, color: AppColors.grey900),
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user