/// Page: Model Houses Page /// /// Displays model house library and design requests following html/nha-mau.html. library; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:worker/core/constants/ui_constants.dart'; import 'package:worker/core/router/app_router.dart'; import 'package:worker/core/theme/colors.dart'; import 'package:worker/features/showrooms/domain/entities/sample_project.dart'; import 'package:worker/features/showrooms/presentation/providers/sample_project_provider.dart'; /// Model Houses Page /// /// Two tabs: /// 1. Thư viện mẫu - Model house library with 360° views /// 2. Yêu cầu thiết kế - Design requests with status tracking class ModelHousesPage extends ConsumerStatefulWidget { const ModelHousesPage({super.key}); @override ConsumerState createState() => _ModelHousesPageState(); } class _ModelHousesPageState extends ConsumerState with SingleTickerProviderStateMixin { late TabController _tabController; @override void initState() { super.initState(); _tabController = TabController(length: 2, vsync: this); } @override void dispose() { _tabController.dispose(); super.dispose(); } void _showInfoDialog() { showDialog( context: context, builder: (context) => AlertDialog( title: const Text( 'Hướng dẫn sử dụng', style: TextStyle(fontWeight: FontWeight.bold), ), content: const SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text('Đây là nội dung hướng dẫn sử dụng cho tính năng Nhà mẫu:'), SizedBox(height: 12), Text( '• Tab "Thư viện Mẫu 360": Là nơi công ty cung cấp các mẫu thiết kế 360° có sẵn để bạn tham khảo.', ), SizedBox(height: 8), Text( '• Tab "Yêu cầu Thiết kế": Là nơi bạn gửi yêu cầu (ticket) để đội ngũ thiết kế của chúng tôi hỗ trợ bạn.', ), SizedBox(height: 8), Text( '• Bấm nút "+" trong tab "Yêu cầu Thiết kế" để tạo một Yêu cầu Thiết kế mới.', ), SizedBox(height: 8), Text( '• Khi yêu cầu hoàn thành, bạn có thể xem link thiết kế 3D trong trang chi tiết yêu cầu.', ), ], ), ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: const Text('Đóng'), ), ], ), ); } void _createNewRequest() { context.push(RouteNames.designRequestCreate); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: AppColors.grey50, appBar: AppBar( backgroundColor: AppColors.white, elevation: AppBarSpecs.elevation, leading: IconButton( icon: const Icon(Icons.arrow_back, color: Colors.black), onPressed: () => Navigator.of(context).pop(), ), centerTitle: false, title: const Text( 'Nhà mẫu', style: TextStyle( color: Colors.black, fontSize: 20, fontWeight: FontWeight.w600, ), ), actions: [ IconButton( icon: const Icon(Icons.info_outline, color: Colors.black), onPressed: _showInfoDialog, ), const SizedBox(width: AppSpacing.sm), ], bottom: TabBar( controller: _tabController, indicatorColor: AppColors.primaryBlue, indicatorWeight: 3, labelColor: AppColors.primaryBlue, labelStyle: const TextStyle( fontSize: 16, fontWeight: FontWeight.w600, ), unselectedLabelColor: AppColors.grey500, unselectedLabelStyle: const TextStyle( fontSize: 16, fontWeight: FontWeight.w600, ), tabs: const [ Tab(text: 'Thư viện mẫu'), Tab(text: 'Yêu cầu thiết kế'), ], ), ), body: TabBarView( controller: _tabController, children: const [_LibraryTab(), _DesignRequestsTab()], ), floatingActionButton: AnimatedBuilder( animation: _tabController, builder: (context, child) { // Show FAB only on Design Requests tab return _tabController.index == 1 ? FloatingActionButton( onPressed: _createNewRequest, backgroundColor: AppColors.primaryBlue, elevation: 4, child: const Icon( Icons.add, color: AppColors.white, size: 28, ), ) : const SizedBox.shrink(); }, ), ); } } /// Library Tab - Model house 360° library class _LibraryTab extends ConsumerWidget { const _LibraryTab(); @override Widget build(BuildContext context, WidgetRef ref) { final sampleProjectsAsync = ref.watch(sampleProjectsListProvider); return sampleProjectsAsync.when( data: (projects) { if (projects.isEmpty) { return const Center( child: Padding( padding: EdgeInsets.all(40), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.home_work_outlined, size: 64, color: AppColors.grey500, ), SizedBox(height: 16), Text( 'Chưa có mẫu nhà nào', style: TextStyle( fontSize: 16, color: AppColors.grey500, ), ), ], ), ), ); } return RefreshIndicator( onRefresh: () => ref.read(sampleProjectsListProvider.notifier).refresh(), child: ListView.builder( padding: const EdgeInsets.all(20), itemCount: projects.length, itemBuilder: (context, index) { final project = projects[index]; return _LibraryCard(project: project); }, ), ); }, loading: () => const Center( child: CircularProgressIndicator(), ), error: (error, stack) => Center( child: Padding( padding: const EdgeInsets.all(40), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon( Icons.error_outline, size: 64, color: AppColors.danger, ), const SizedBox(height: 16), Text( 'Lỗi tải dữ liệu: ${error.toString().replaceAll('Exception: ', '')}', textAlign: TextAlign.center, style: const TextStyle( fontSize: 14, color: AppColors.grey500, ), ), const SizedBox(height: 16), ElevatedButton( onPressed: () => ref.invalidate(sampleProjectsListProvider), child: const Text('Thử lại'), ), ], ), ), ), ); } } /// Library Card Widget class _LibraryCard extends StatelessWidget { const _LibraryCard({required this.project}); final SampleProject project; @override Widget build(BuildContext context) { return Card( elevation: 2, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), margin: const EdgeInsets.only(bottom: 20), child: InkWell( onTap: () { context.push('/model-houses/${project.id}'); }, borderRadius: BorderRadius.circular(12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Image with 360 badge Stack( children: [ ClipRRect( borderRadius: const BorderRadius.vertical( top: Radius.circular(12), ), child: project.thumbnailUrl != null ? CachedNetworkImage( imageUrl: project.thumbnailUrl!, width: double.infinity, height: 200, fit: BoxFit.cover, placeholder: (context, url) => Container( height: 200, color: AppColors.grey100, child: const Center(child: CircularProgressIndicator()), ), errorWidget: (context, url, error) => Container( height: 200, color: AppColors.grey100, child: const Icon( Icons.image_not_supported, size: 48, color: AppColors.grey500, ), ), ) : Container( height: 200, color: AppColors.grey100, child: const Icon( Icons.home_work, size: 48, color: AppColors.grey500, ), ), ), if (project.has360View) Positioned( top: 12, right: 12, child: Container( padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 6, ), decoration: BoxDecoration( color: AppColors.primaryBlue.withValues(alpha: 0.9), borderRadius: BorderRadius.circular(16), ), child: const Text( 'Xem 360°', style: TextStyle( color: AppColors.white, fontSize: 12, fontWeight: FontWeight.w600, ), ), ), ), ], ), // Content Padding( padding: const EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Title Text( project.projectName, style: const TextStyle( fontSize: 18, fontWeight: FontWeight.w700, color: AppColors.grey900, ), ), if (project.plainDescription.isNotEmpty) ...[ const SizedBox(height: 12), // Description Text( project.plainDescription, style: const TextStyle( fontSize: 14, color: AppColors.grey500, height: 1.5, ), maxLines: 3, overflow: TextOverflow.ellipsis, ), ], ], ), ), ], ), ), ); } } /// Design Requests Tab class _DesignRequestsTab extends StatelessWidget { const _DesignRequestsTab(); @override Widget build(BuildContext context) { return ListView( padding: const EdgeInsets.all(20), children: const [ _RequestCard( code: '#YC001', status: DesignRequestStatus.completed, date: '20/10/2024', description: 'Thiết kế nhà phố 3 tầng - Anh Minh (Quận 7)', ), _RequestCard( code: '#YC002', status: DesignRequestStatus.designing, date: '25/10/2024', description: 'Cải tạo căn hộ chung cư - Chị Lan (Quận 2)', ), _RequestCard( code: '#YC003', status: DesignRequestStatus.pending, date: '28/10/2024', description: 'Thiết kế biệt thự 2 tầng - Anh Đức (Bình Dương)', ), _RequestCard( code: '#YC004', status: DesignRequestStatus.pending, date: '01/11/2024', description: 'Thiết kế cửa hàng kinh doanh - Chị Mai (Quận 1)', ), ], ); } } /// Design Request Status enum DesignRequestStatus { pending, designing, completed } /// Request Card Widget class _RequestCard extends StatelessWidget { const _RequestCard({ required this.code, required this.status, required this.date, required this.description, }); final String code; final DesignRequestStatus status; final String date; final String description; Color _getStatusColor() { switch (status) { case DesignRequestStatus.pending: return const Color(0xFFffc107); // Warning yellow case DesignRequestStatus.designing: return const Color(0xFF3730a3); // Indigo case DesignRequestStatus.completed: return const Color(0xFF065f46); // Success green } } Color _getStatusBackgroundColor() { switch (status) { case DesignRequestStatus.pending: return const Color(0xFFfef3c7); // Light yellow case DesignRequestStatus.designing: return const Color(0xFFe0e7ff); // Light indigo case DesignRequestStatus.completed: return const Color(0xFFd1fae5); // Light green } } String _getStatusText() { switch (status) { case DesignRequestStatus.pending: return 'CHỜ TIẾP NHẬN'; case DesignRequestStatus.designing: return 'ĐANG THIẾT KẾ'; case DesignRequestStatus.completed: return 'HOÀN THÀNH'; } } @override Widget build(BuildContext context) { return Card( elevation: 2, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), margin: const EdgeInsets.only(bottom: 16), child: InkWell( onTap: () { context.push( '/model-houses/design-request/${code.replaceAll('#', '')}', ); }, borderRadius: BorderRadius.circular(12), child: Padding( padding: const EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Header: Code and Status Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Mã yêu cầu: $code', style: const TextStyle( fontSize: 16, fontWeight: FontWeight.w700, color: AppColors.grey900, ), ), Container( padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 6, ), decoration: BoxDecoration( color: _getStatusBackgroundColor(), borderRadius: BorderRadius.circular(20), ), child: Text( _getStatusText(), style: TextStyle( fontSize: 12, fontWeight: FontWeight.w600, color: _getStatusColor(), ), ), ), ], ), const SizedBox(height: 8), // Date Text( 'Ngày gửi: $date', style: const TextStyle(fontSize: 14, color: AppColors.grey500), ), const SizedBox(height: 8), // Description Text( description, style: const TextStyle(fontSize: 14, color: AppColors.grey900), ), ], ), ), ), ); } }