This commit is contained in:
2025-10-28 23:46:07 +07:00
parent 73b77c27de
commit f32e1c16fb
10 changed files with 333 additions and 142 deletions

View File

@@ -195,7 +195,7 @@ class _ProductDetailPageState extends ConsumerState<ProductDetailPage> {
FilledButton.icon(
onPressed: _onRefresh,
icon: const Icon(Icons.refresh),
label: const Text('Retry'),
label: const Text('Thử lại'),
),
],
),
@@ -259,7 +259,7 @@ class _ProductDetailPageState extends ConsumerState<ProductDetailPage> {
FilledButton.icon(
onPressed: () => Navigator.pop(context),
icon: const Icon(Icons.arrow_back),
label: const Text('Go Back'),
label: const Text('Quay lại'),
),
],
),
@@ -368,7 +368,7 @@ class _ProductDetailPageState extends ConsumerState<ProductDetailPage> {
: selectedStage;
if (stageToShow == null) {
return const Center(child: Text('No stage selected'));
return const Center(child: Text('Chưa chọn công đoạn'));
}
return SingleChildScrollView(
@@ -485,7 +485,7 @@ class _ProductDetailPageState extends ConsumerState<ProductDetailPage> {
child: OutlinedButton.icon(
onPressed: () => _printQuantities(stageToShow),
icon: const Icon(Icons.print),
label: const Text('Print'),
label: const Text('In'),
style: OutlinedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 16),
),
@@ -495,7 +495,7 @@ class _ProductDetailPageState extends ConsumerState<ProductDetailPage> {
child: FilledButton.icon(
onPressed: () => _addNewQuantities(stageToShow),
icon: const Icon(Icons.save),
label: const Text('Save'),
label: const Text('Lưu'),
style: FilledButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 16),
),
@@ -517,7 +517,7 @@ class _ProductDetailPageState extends ConsumerState<ProductDetailPage> {
// TODO: Implement print functionality
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Print functionality coming soon'),
content: Text('Tính năng in đang phát triển'),
duration: Duration(seconds: 2),
),
);
@@ -535,7 +535,7 @@ class _ProductDetailPageState extends ConsumerState<ProductDetailPage> {
issuedQuantity == 0 && issuedWeight == 0.0) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Please enter at least one quantity or weight value'),
content: Text('Vui lòng nhập ít nhất một giá trị số lượng hoặc khối lượng'),
backgroundColor: Colors.orange,
),
);
@@ -546,7 +546,7 @@ class _ProductDetailPageState extends ConsumerState<ProductDetailPage> {
if (_selectedEmployee == null || _selectedWarehouseUser == null) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Please select both Employee and Warehouse User'),
content: Text('Vui lòng chọn cả Nhân viên và Người dùng kho'),
backgroundColor: Colors.orange,
),
);
@@ -607,7 +607,7 @@ class _ProductDetailPageState extends ConsumerState<ProductDetailPage> {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Failed to add quantities: ${failure.message}'),
content: Text('Lỗi khi thêm số lượng: ${failure.message}'),
backgroundColor: Colors.red,
duration: const Duration(seconds: 3),
),
@@ -619,7 +619,7 @@ class _ProductDetailPageState extends ConsumerState<ProductDetailPage> {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Quantities added successfully!'),
content: Text('Đã thêm số lượng thành công!'),
backgroundColor: Colors.green,
duration: const Duration(seconds: 2),
),
@@ -644,7 +644,7 @@ class _ProductDetailPageState extends ConsumerState<ProductDetailPage> {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Error: ${e.toString()}'),
content: Text('Lỗi: ${e.toString()}'),
backgroundColor: Colors.red,
duration: const Duration(seconds: 3),
),
@@ -892,7 +892,7 @@ class _ProductDetailPageState extends ConsumerState<ProductDetailPage> {
vertical: 12,
),
),
hint: Text('Select $label'),
hint: Text('Chọn $label'),
items: users.map((user) {
return DropdownMenuItem<UserEntity>(
value: user,
@@ -921,61 +921,4 @@ class _ProductDetailPageState extends ConsumerState<ProductDetailPage> {
}
}
Widget _buildStatusCards(ProductStageEntity stage, ThemeData theme) {
return Row(
children: [
Expanded(
child: Card(
color: stage.hasPassedQuantity
? Colors.green.withValues(alpha: 0.1)
: Colors.grey.withValues(alpha: 0.1),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
Icon(
stage.hasPassedQuantity ? Icons.check_circle : Icons.cancel,
color: stage.hasPassedQuantity ? Colors.green : Colors.grey,
size: 32,
),
const SizedBox(height: 8),
Text(
'Has Passed',
style: theme.textTheme.bodySmall,
textAlign: TextAlign.center,
),
],
),
),
),
),
const SizedBox(width: 8),
Expanded(
child: Card(
color: stage.hasIssuedQuantity
? Colors.blue.withValues(alpha: 0.1)
: Colors.grey.withValues(alpha: 0.1),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
Icon(
stage.hasIssuedQuantity ? Icons.check_circle : Icons.cancel,
color: stage.hasIssuedQuantity ? Colors.blue : Colors.grey,
size: 32,
),
const SizedBox(height: 8),
Text(
'Has Issued',
style: theme.textTheme.bodySmall,
textAlign: TextAlign.center,
),
],
),
),
),
),
],
);
}
}

View File

@@ -121,7 +121,7 @@ class _ProductsPageState extends ConsumerState<ProductsPage>
const SizedBox(width: 12),
const Expanded(
child: Text(
'Scan Barcode',
'Quét mã vạch',
style: TextStyle(
color: Colors.white,
fontSize: 18,
@@ -161,7 +161,7 @@ class _ProductsPageState extends ConsumerState<ProductsPage>
padding: const EdgeInsets.all(16),
color: Colors.grey.shade900,
child: const Text(
'Position the Code 128 barcode within the frame to scan',
'Đặt mã vạch Code 128 vào khung để quét',
style: TextStyle(
color: Colors.white70,
fontSize: 14,
@@ -199,7 +199,7 @@ class _ProductsPageState extends ConsumerState<ProductsPage>
// Invalid barcode format
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Invalid barcode format: "$barcode"'),
content: Text('Định dạng mã vạch không hợp lệ: "$barcode"'),
backgroundColor: Colors.red,
action: SnackBarAction(
label: 'OK',
@@ -212,11 +212,12 @@ class _ProductsPageState extends ConsumerState<ProductsPage>
}
// Navigate to product detail with productId and optional stageId
// Use the currently selected tab's operation type
context.goToProductDetail(
warehouseId: widget.warehouseId,
productId: productId,
warehouseName: widget.warehouseName,
operationType: widget.operationType,
operationType: _currentOperationType,
stageId: stageId,
);
}
@@ -250,7 +251,7 @@ class _ProductsPageState extends ConsumerState<ProductsPage>
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Products',
'Sản phẩm',
style: textTheme.titleMedium,
),
Text(
@@ -265,7 +266,7 @@ class _ProductsPageState extends ConsumerState<ProductsPage>
IconButton(
icon: const Icon(Icons.refresh),
onPressed: _onRefresh,
tooltip: 'Refresh',
tooltip: 'Làm mới',
),
],
bottom: TabBar(
@@ -273,11 +274,11 @@ class _ProductsPageState extends ConsumerState<ProductsPage>
tabs: const [
Tab(
icon: Icon(Icons.arrow_downward),
text: 'Import',
text: 'Nhập kho',
),
Tab(
icon: Icon(Icons.arrow_upward),
text: 'Export',
text: 'Xuất kho',
),
],
),
@@ -291,7 +292,7 @@ class _ProductsPageState extends ConsumerState<ProductsPage>
floatingActionButton: products.isNotEmpty
? FloatingActionButton(
onPressed: _showBarcodeScanner,
tooltip: 'Scan Barcode',
tooltip: 'Quét mã vạch',
child: const Icon(Icons.qr_code_scanner),
)
: null,
@@ -336,14 +337,14 @@ class _ProductsPageState extends ConsumerState<ProductsPage>
children: [
Text(
_currentOperationType == 'import'
? 'Import Products'
: 'Export Products',
? 'Nhập kho'
: 'Xuất kho',
style: theme.textTheme.titleSmall?.copyWith(
fontWeight: FontWeight.bold,
),
),
Text(
'Warehouse: ${widget.warehouseName}',
'Kho: ${widget.warehouseName}',
style: theme.textTheme.bodySmall?.copyWith(
color: theme.colorScheme.onSurfaceVariant,
),
@@ -385,7 +386,7 @@ class _ProductsPageState extends ConsumerState<ProductsPage>
children: [
CircularProgressIndicator(),
SizedBox(height: 16),
Text('Loading products...'),
Text('Đang tải sản phẩm...'),
],
),
);
@@ -406,7 +407,7 @@ class _ProductsPageState extends ConsumerState<ProductsPage>
),
const SizedBox(height: 16),
Text(
'Error',
'Lỗi',
style: theme.textTheme.titleLarge?.copyWith(
color: theme.colorScheme.error,
),
@@ -421,7 +422,7 @@ class _ProductsPageState extends ConsumerState<ProductsPage>
FilledButton.icon(
onPressed: _onRefresh,
icon: const Icon(Icons.refresh),
label: const Text('Retry'),
label: const Text('Thử lại'),
),
],
),
@@ -444,12 +445,12 @@ class _ProductsPageState extends ConsumerState<ProductsPage>
),
const SizedBox(height: 16),
Text(
'No Products',
'Không có sản phẩm',
style: theme.textTheme.titleLarge,
),
const SizedBox(height: 8),
Text(
'No products found for this warehouse and operation type.',
'Không tìm thấy sản phẩm nào cho kho và loại thao tác này.',
textAlign: TextAlign.center,
style: theme.textTheme.bodyMedium?.copyWith(
color: theme.colorScheme.onSurfaceVariant,
@@ -459,7 +460,7 @@ class _ProductsPageState extends ConsumerState<ProductsPage>
FilledButton.icon(
onPressed: _onRefresh,
icon: const Icon(Icons.refresh),
label: const Text('Refresh'),
label: const Text('Làm mới'),
),
],
),
@@ -478,12 +479,12 @@ class _ProductsPageState extends ConsumerState<ProductsPage>
return ProductListItem(
product: product,
onTap: () {
// Navigate to product detail page
// Navigate to product detail page with current tab's operation type
context.goToProductDetail(
warehouseId: widget.warehouseId,
productId: product.id,
warehouseName: widget.warehouseName,
operationType: widget.operationType,
operationType: _currentOperationType,
);
},
);