update theme

This commit is contained in:
Phuoc Nguyen
2025-12-02 15:20:54 +07:00
parent 12bd70479c
commit 49a41d24eb
78 changed files with 3263 additions and 2756 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -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,
),
),
],

View File

@@ -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,
),
],

View File

@@ -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;
}
}

View File

@@ -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'),
),

View File

@@ -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(

View File

@@ -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,

View File

@@ -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,
),
),
),
],