add price policy
This commit is contained in:
@@ -0,0 +1,231 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import '../../../../core/constants/ui_constants.dart';
|
||||
import '../../../../core/theme/colors.dart';
|
||||
import '../../domain/entities/price_document.dart';
|
||||
import '../providers/price_documents_provider.dart';
|
||||
import '../widgets/document_card.dart';
|
||||
|
||||
/// Price policy page with tabs for policies and price lists
|
||||
class PricePolicyPage extends ConsumerStatefulWidget {
|
||||
const PricePolicyPage({super.key});
|
||||
|
||||
@override
|
||||
ConsumerState<PricePolicyPage> createState() => _PricePolicyPageState();
|
||||
}
|
||||
|
||||
class _PricePolicyPageState extends ConsumerState<PricePolicyPage>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late TabController _tabController;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_tabController = TabController(length: 2, vsync: this);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_tabController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: AppColors.grey50,
|
||||
appBar: AppBar(
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.arrow_back, color: Colors.black),
|
||||
onPressed: () => context.pop(),
|
||||
),
|
||||
title: const Text(
|
||||
'Chính sách giá',
|
||||
style: TextStyle(color: Colors.black),
|
||||
),
|
||||
elevation: AppBarSpecs.elevation,
|
||||
backgroundColor: AppColors.white,
|
||||
foregroundColor: AppColors.grey900,
|
||||
centerTitle: false,
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.info_outline, color: Colors.black),
|
||||
onPressed: _showInfoDialog,
|
||||
),
|
||||
const SizedBox(width: AppSpacing.sm),
|
||||
],
|
||||
bottom: TabBar(
|
||||
controller: _tabController,
|
||||
labelColor: AppColors.white,
|
||||
unselectedLabelColor: AppColors.grey900,
|
||||
indicatorSize: TabBarIndicatorSize.tab,
|
||||
indicator: BoxDecoration(
|
||||
color: AppColors.primaryBlue,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
labelStyle: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
unselectedLabelStyle: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.normal,
|
||||
),
|
||||
tabs: const [
|
||||
Tab(text: 'Chính sách giá'),
|
||||
Tab(text: 'Bảng giá'),
|
||||
],
|
||||
),
|
||||
),
|
||||
body: TabBarView(
|
||||
controller: _tabController,
|
||||
children: [
|
||||
// Policy tab
|
||||
_buildDocumentList(DocumentCategory.policy),
|
||||
// Price list tab
|
||||
_buildDocumentList(DocumentCategory.priceList),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDocumentList(DocumentCategory category) {
|
||||
final documentsAsync = ref.watch(filteredPriceDocumentsProvider(category));
|
||||
|
||||
return documentsAsync.when(
|
||||
data: (documents) {
|
||||
if (documents.isEmpty) {
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.description_outlined,
|
||||
size: 64,
|
||||
color: AppColors.grey500,
|
||||
),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
Text(
|
||||
'Chưa có tài liệu',
|
||||
style: TextStyle(fontSize: 16, color: AppColors.grey500),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return RefreshIndicator(
|
||||
onRefresh: () async {
|
||||
// Refresh documents from repository
|
||||
ref.invalidate(filteredPriceDocumentsProvider(category));
|
||||
await Future<void>.delayed(const Duration(milliseconds: 500));
|
||||
},
|
||||
child: ListView.separated(
|
||||
padding: const EdgeInsets.all(AppSpacing.md),
|
||||
itemCount: documents.length,
|
||||
separatorBuilder: (context, index) =>
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
itemBuilder: (context, index) {
|
||||
final document = documents[index];
|
||||
return DocumentCard(
|
||||
document: document,
|
||||
onDownload: () => _handleDownload(document),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
loading: () => const Center(child: CircularProgressIndicator()),
|
||||
error: (error, stack) => Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.error_outline, size: 64, color: AppColors.danger),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
Text(
|
||||
'Không thể tải tài liệu',
|
||||
style: TextStyle(fontSize: 16, color: AppColors.grey500),
|
||||
),
|
||||
const SizedBox(height: AppSpacing.sm),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
ref.invalidate(filteredPriceDocumentsProvider(category));
|
||||
},
|
||||
child: const Text('Thử lại'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _handleDownload(PriceDocument document) {
|
||||
// In real app, this would trigger actual download
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('Đang tải: ${document.title}'),
|
||||
duration: const Duration(seconds: 2),
|
||||
backgroundColor: AppColors.primaryBlue,
|
||||
behavior: SnackBarBehavior.floating,
|
||||
),
|
||||
);
|
||||
|
||||
// Simulate download
|
||||
// TODO: Implement actual file download functionality
|
||||
// - Use url_launcher or dio to download file
|
||||
// - Show progress indicator
|
||||
// - Save to device storage
|
||||
}
|
||||
|
||||
void _showInfoDialog() {
|
||||
showDialog<void>(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
title: const Text(
|
||||
'Hướng dẫn sử dụng',
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
'Đây là nội dung hướng dẫn sử dụng cho tính năng Chính sách giá:',
|
||||
),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
_buildInfoItem(
|
||||
'Chọn tab "Chính sách giá" để xem các chính sách giá hiện hành',
|
||||
),
|
||||
_buildInfoItem(
|
||||
'Chọn tab "Bảng giá" để tải về bảng giá chi tiết sản phẩm',
|
||||
),
|
||||
_buildInfoItem('Nhấn nút "Tải về" để download file PDF/Excel'),
|
||||
_buildInfoItem('Các bảng giá được cập nhật định kỳ hàng tháng'),
|
||||
_buildInfoItem('Liên hệ sales để được tư vấn giá tốt nhất'),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: const Text('Đóng'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildInfoItem(String text) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: AppSpacing.xs),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text('• ', style: TextStyle(fontSize: 16)),
|
||||
Expanded(child: Text(text)),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user