This commit is contained in:
Phuoc Nguyen
2025-10-27 09:33:00 +07:00
parent 380ad60ee5
commit 830ef7e2a2
7 changed files with 1408 additions and 1 deletions

View File

@@ -0,0 +1,249 @@
/// Widget: Order Card
///
/// Displays order information in a card format.
library;
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:intl/intl.dart';
import 'package:worker/core/database/models/enums.dart';
import 'package:worker/core/theme/colors.dart';
import 'package:worker/features/orders/data/models/order_model.dart';
/// Order Card Widget
///
/// Displays order details in a card with status indicator.
class OrderCard extends StatelessWidget {
/// Order to display
final OrderModel order;
/// Tap callback
final VoidCallback? onTap;
const OrderCard({
required this.order,
this.onTap,
super.key,
});
@override
Widget build(BuildContext context) {
final currencyFormatter = NumberFormat.currency(
locale: 'vi_VN',
symbol: 'đ',
decimalDigits: 0,
);
return Card(
margin: const EdgeInsets.only(bottom: 12),
elevation: 1,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
clipBehavior: Clip.antiAlias,
child: InkWell(
onTap: onTap,
child: Container(
decoration: BoxDecoration(
border: Border(
left: BorderSide(
color: _getStatusColor(order.status),
width: 4,
),
),
),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Order number and amount row
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Order number
Text(
'#${order.orderNumber}',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w700,
color: AppColors.grey900,
),
),
// Amount
Text(
currencyFormatter.format(order.finalAmount),
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w700,
color: AppColors.primaryBlue,
),
),
],
),
const SizedBox(height: 12),
// Order details
_buildDetailRow(
'Ngày đặt:',
_formatDate(order.createdAt),
),
const SizedBox(height: 6),
_buildDetailRow(
'Ngày giao:',
order.expectedDeliveryDate != null
? _formatDate(order.expectedDeliveryDate!)
: 'Chưa xác định',
),
const SizedBox(height: 6),
_buildDetailRow(
'Địa chỉ:',
_getShortAddress(),
),
const SizedBox(height: 12),
// Status badge
_buildStatusBadge(),
],
),
),
),
),
);
}
/// Build detail row
Widget _buildDetailRow(String label, String value) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
label,
style: const TextStyle(
fontSize: 14,
color: AppColors.grey500,
),
),
const SizedBox(width: 8),
Expanded(
child: Text(
value,
style: const TextStyle(
fontSize: 14,
color: AppColors.grey900,
),
),
),
],
);
}
/// Build status badge
Widget _buildStatusBadge() {
return Container(
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 6,
),
decoration: BoxDecoration(
color: _getStatusColor(order.status).withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(16),
border: Border.all(
color: _getStatusColor(order.status).withValues(alpha: 0.3),
width: 1,
),
),
child: Text(
_getStatusText(order.status),
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w600,
color: _getStatusColor(order.status),
),
),
);
}
/// Get status color
Color _getStatusColor(OrderStatus status) {
switch (status) {
case OrderStatus.draft:
return AppColors.grey500;
case OrderStatus.pending:
return const Color(0xFFF59E0B); // warning/pending color
case OrderStatus.confirmed:
return const Color(0xFFF59E0B); // warning/pending color
case OrderStatus.processing:
return AppColors.info;
case OrderStatus.shipped:
return const Color(0xFF3B82F6); // blue
case OrderStatus.delivered:
return const Color(0xFF10B981); // green
case OrderStatus.completed:
return AppColors.success;
case OrderStatus.cancelled:
return AppColors.danger;
case OrderStatus.refunded:
return const Color(0xFFF97316); // orange
}
}
/// Get status text in Vietnamese
String _getStatusText(OrderStatus status) {
switch (status) {
case OrderStatus.draft:
return 'Nháp';
case OrderStatus.pending:
return 'Chờ xác nhận';
case OrderStatus.confirmed:
return 'Đã xác nhận';
case OrderStatus.processing:
return 'Đang xử lý';
case OrderStatus.shipped:
return 'Đang giao';
case OrderStatus.delivered:
return 'Đã giao';
case OrderStatus.completed:
return 'Hoàn thành';
case OrderStatus.cancelled:
return 'Đã hủy';
case OrderStatus.refunded:
return 'Đã hoàn tiền';
}
}
/// Format date to dd/MM/yyyy
String _formatDate(DateTime date) {
return DateFormat('dd/MM/yyyy').format(date);
}
/// Get short address (city or district, city)
String _getShortAddress() {
if (order.shippingAddress == null) {
return 'Chưa có địa chỉ';
}
try {
final addressJson = jsonDecode(order.shippingAddress!);
final city = addressJson['city'] as String?;
final district = addressJson['district'] as String?;
if (district != null && city != null) {
return '$district, $city';
} else if (city != null) {
return city;
} else {
return 'Chưa có địa chỉ';
}
} catch (e) {
return 'Chưa có địa chỉ';
}
}
}