From 597c6a0e573b217d84713f1195f770cfa09e9c5e Mon Sep 17 00:00:00 2001 From: Phuoc Nguyen Date: Tue, 9 Dec 2025 11:17:32 +0700 Subject: [PATCH] fix homepage news, invoice --- .../home/presentation/pages/home_page.dart | 3 +- .../invoices/data/models/invoice_model.dart | 5 + .../invoices/domain/entities/invoice.dart | 3 + .../pages/invoice_detail_page.dart | 8 +- .../presentation/widgets/product_card.dart | 20 ++-- .../product_detail/image_gallery_section.dart | 101 ++++-------------- .../pages/submission_create_page.dart | 8 +- pubspec.yaml | 2 +- 8 files changed, 51 insertions(+), 99 deletions(-) diff --git a/lib/features/home/presentation/pages/home_page.dart b/lib/features/home/presentation/pages/home_page.dart index 6bafa85..42866e4 100644 --- a/lib/features/home/presentation/pages/home_page.dart +++ b/lib/features/home/presentation/pages/home_page.dart @@ -131,7 +131,8 @@ class _HomePageState extends ConsumerState { promotions: promotions, onPromotionTap: (promotion) { // Navigate to promotion details - context.push('/promotions/${promotion.id}'); + context.push('/news/${promotion.id}'); + }, ) : const SizedBox.shrink(), diff --git a/lib/features/invoices/data/models/invoice_model.dart b/lib/features/invoices/data/models/invoice_model.dart index 8e20993..52fb13a 100644 --- a/lib/features/invoices/data/models/invoice_model.dart +++ b/lib/features/invoices/data/models/invoice_model.dart @@ -87,6 +87,7 @@ class BuyerInfoModel { final String? wardCode; final String? cityName; final String? wardName; + final String? customerName; const BuyerInfoModel({ this.name, @@ -100,6 +101,7 @@ class BuyerInfoModel { this.wardCode, this.cityName, this.wardName, + this.customerName, }); factory BuyerInfoModel.fromJson(Map json) { @@ -115,6 +117,7 @@ class BuyerInfoModel { wardCode: json['ward_code'] as String?, cityName: json['city_name'] as String?, wardName: json['ward_name'] as String?, + customerName: json['customer_name'] as String?, ); } @@ -130,6 +133,7 @@ class BuyerInfoModel { 'ward_code': wardCode, 'city_name': cityName, 'ward_name': wardName, + 'customer_name': customerName, }; BuyerInfo toEntity() => BuyerInfo( @@ -144,6 +148,7 @@ class BuyerInfoModel { wardCode: wardCode, cityName: cityName, wardName: wardName, + customerName: customerName, ); } diff --git a/lib/features/invoices/domain/entities/invoice.dart b/lib/features/invoices/domain/entities/invoice.dart index 77aca90..68f6b49 100644 --- a/lib/features/invoices/domain/entities/invoice.dart +++ b/lib/features/invoices/domain/entities/invoice.dart @@ -75,6 +75,7 @@ class BuyerInfo extends Equatable { final String? wardCode; final String? cityName; final String? wardName; + final String? customerName; const BuyerInfo({ this.name, @@ -88,6 +89,7 @@ class BuyerInfo extends Equatable { this.wardCode, this.cityName, this.wardName, + this.customerName }); /// Get formatted full address @@ -118,6 +120,7 @@ class BuyerInfo extends Equatable { wardCode, cityName, wardName, + customerName ]; } diff --git a/lib/features/invoices/presentation/pages/invoice_detail_page.dart b/lib/features/invoices/presentation/pages/invoice_detail_page.dart index c36d6de..cfc583e 100644 --- a/lib/features/invoices/presentation/pages/invoice_detail_page.dart +++ b/lib/features/invoices/presentation/pages/invoice_detail_page.dart @@ -294,10 +294,10 @@ class InvoiceDetailPage extends ConsumerWidget { List<_InfoLine> _buildBuyerInfoLines(Invoice invoice) { return [ - if (invoice.buyerInfo!.name != null) - _InfoLine(label: 'Người mua hàng', value: invoice.buyerInfo!.name!), - if (invoice.customerName != null) - _InfoLine(label: 'Tên đơn vị', value: invoice.customerName!), + if (invoice.buyerInfo!.customerName != null) + _InfoLine(label: 'Người mua hàng', value: invoice.buyerInfo!.customerName!), + if (invoice.buyerInfo!.addressTitle != null) + _InfoLine(label: 'Tên đơn vị', value: invoice.buyerInfo!.addressTitle!), if (invoice.buyerInfo!.taxCode != null) _InfoLine(label: 'Mã số thuế', value: invoice.buyerInfo!.taxCode!), if (invoice.buyerInfo!.fullAddress.isNotEmpty) diff --git a/lib/features/products/presentation/widgets/product_card.dart b/lib/features/products/presentation/widgets/product_card.dart index 6de577e..b45f1ea 100644 --- a/lib/features/products/presentation/widgets/product_card.dart +++ b/lib/features/products/presentation/widgets/product_card.dart @@ -9,6 +9,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:intl/intl.dart'; import 'package:shimmer/shimmer.dart'; +import 'package:url_launcher/url_launcher.dart'; import 'package:worker/core/constants/ui_constants.dart'; import 'package:worker/core/theme/colors.dart'; import 'package:worker/features/products/domain/entities/product.dart'; @@ -273,17 +274,14 @@ class ProductCard extends ConsumerWidget { width: double.infinity, height: 36.0, child: OutlinedButton.icon( - onPressed: () { - // TODO: Open 360 view in browser - // For now, show a message - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text( - 'Đang phát triển tính năng xem 360°', - ), - duration: Duration(seconds: 2), - ), - ); + onPressed: () async { + final url = Uri.parse(product.customLink360!); + if (await canLaunchUrl(url)) { + await launchUrl( + url, + mode: LaunchMode.inAppWebView, + ); + } }, style: OutlinedButton.styleFrom( foregroundColor: colorScheme.primary, diff --git a/lib/features/products/presentation/widgets/product_detail/image_gallery_section.dart b/lib/features/products/presentation/widgets/product_detail/image_gallery_section.dart index b62918a..fae61b8 100644 --- a/lib/features/products/presentation/widgets/product_detail/image_gallery_section.dart +++ b/lib/features/products/presentation/widgets/product_detail/image_gallery_section.dart @@ -7,6 +7,7 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:shimmer/shimmer.dart'; +import 'package:url_launcher/url_launcher.dart'; import 'package:worker/core/constants/ui_constants.dart'; import 'package:worker/features/products/domain/entities/product.dart'; @@ -18,7 +19,6 @@ import 'package:worker/features/products/domain/entities/product.dart'; /// - Image indicators (dots) /// - Thumbnail gallery row (horizontal scroll) class ImageGallerySection extends StatefulWidget { - const ImageGallerySection({super.key, required this.product}); final Product product; @@ -40,22 +40,15 @@ class _ImageGallerySectionState extends State { setState(() { _currentImageIndex = index; }); - _pageController.animateToPage( - index, - duration: AppDuration.medium, - curve: Curves.easeInOut, - ); + _pageController.animateToPage(index, duration: AppDuration.medium, curve: Curves.easeInOut); } - void _open360View() { + void _open360View() async { if (widget.product.customLink360 != null) { - // TODO: Open in browser - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('Đang phát triển tính năng xem 360°'), - duration: Duration(seconds: 2), - ), - ); + final url = Uri.parse(widget.product.customLink360!); + if (await canLaunchUrl(url)) { + await launchUrl(url, mode: LaunchMode.inAppWebView); + } } } @@ -108,11 +101,7 @@ class _ImageGallerySectionState extends State { ), errorWidget: (context, url, error) => Container( color: colorScheme.surfaceContainerHighest, - child: Icon( - FontAwesomeIcons.image, - size: 64, - color: colorScheme.onSurfaceVariant, - ), + child: Icon(FontAwesomeIcons.image, size: 64, color: colorScheme.onSurfaceVariant), ), ), ); @@ -131,10 +120,7 @@ class _ImageGallerySectionState extends State { onTap: _open360View, borderRadius: BorderRadius.circular(20), child: Container( - padding: const EdgeInsets.symmetric( - horizontal: 12, - vertical: 8, - ), + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), child: Row( mainAxisSize: MainAxisSize.min, children: [ @@ -144,11 +130,7 @@ class _ImageGallerySectionState extends State { builder: (context, double value, child) { return Transform.rotate( angle: value * 2 * 3.14159, - child: Icon( - FontAwesomeIcons.arrowsRotate, - size: 10, - color: colorScheme.surface, - ), + child: Icon(FontAwesomeIcons.arrowsRotate, size: 10, color: colorScheme.surface), ); }, onEnd: () { @@ -159,11 +141,7 @@ class _ImageGallerySectionState extends State { const SizedBox(width: 6), Text( '360°', - style: TextStyle( - color: colorScheme.surface, - fontSize: 12, - fontWeight: FontWeight.w600, - ), + style: TextStyle(color: colorScheme.surface, fontSize: 12, fontWeight: FontWeight.w600), ), ], ), @@ -245,9 +223,7 @@ class _ImageGallerySectionState extends State { decoration: BoxDecoration( borderRadius: BorderRadius.circular(8), border: Border.all( - color: isActive - ? colorScheme.primary - : Colors.transparent, + color: isActive ? colorScheme.primary : Colors.transparent, width: 2, ), ), @@ -256,15 +232,10 @@ class _ImageGallerySectionState extends State { child: CachedNetworkImage( imageUrl: imageUrl, fit: BoxFit.cover, - placeholder: (context, url) => - Container(color: colorScheme.surfaceContainerHighest), + placeholder: (context, url) => Container(color: colorScheme.surfaceContainerHighest), errorWidget: (context, url, error) => Container( color: colorScheme.surfaceContainerHighest, - child: Icon( - FontAwesomeIcons.image, - size: 20, - color: colorScheme.onSurfaceVariant, - ), + child: Icon(FontAwesomeIcons.image, size: 20, color: colorScheme.onSurfaceVariant), ), ), ), @@ -285,12 +256,7 @@ class _ImageGallerySectionState extends State { /// Image Lightbox for full-screen image viewing class _ImageLightbox extends StatefulWidget { - - const _ImageLightbox({ - required this.images, - required this.imageCaptions, - required this.initialIndex, - }); + const _ImageLightbox({required this.images, required this.imageCaptions, required this.initialIndex}); final List images; final Map imageCaptions; final int initialIndex; @@ -318,19 +284,13 @@ class _ImageLightboxState extends State<_ImageLightbox> { void _nextImage() { if (_currentIndex < widget.images.length - 1) { - _pageController.nextPage( - duration: AppDuration.medium, - curve: Curves.easeInOut, - ); + _pageController.nextPage(duration: AppDuration.medium, curve: Curves.easeInOut); } } void _previousImage() { if (_currentIndex > 0) { - _pageController.previousPage( - duration: AppDuration.medium, - curve: Curves.easeInOut, - ); + _pageController.previousPage(duration: AppDuration.medium, curve: Curves.easeInOut); } } @@ -372,11 +332,8 @@ class _ImageLightboxState extends State<_ImageLightbox> { child: CachedNetworkImage( imageUrl: widget.images[index], fit: BoxFit.contain, - errorWidget: (context, url, error) => Icon( - FontAwesomeIcons.circleExclamation, - color: colorScheme.surface, - size: 64, - ), + errorWidget: (context, url, error) => + Icon(FontAwesomeIcons.circleExclamation, color: colorScheme.surface, size: 64), ), ), ); @@ -393,15 +350,9 @@ class _ImageLightboxState extends State<_ImageLightbox> { bottom: 0, child: Center( child: IconButton( - icon: Icon( - FontAwesomeIcons.chevronLeft, - color: colorScheme.surface, - size: 32, - ), + icon: Icon(FontAwesomeIcons.chevronLeft, color: colorScheme.surface, size: 32), onPressed: _previousImage, - style: IconButton.styleFrom( - backgroundColor: Colors.white.withAlpha(51), - ), + style: IconButton.styleFrom(backgroundColor: Colors.white.withAlpha(51)), ), ), ), @@ -414,15 +365,9 @@ class _ImageLightboxState extends State<_ImageLightbox> { bottom: 0, child: Center( child: IconButton( - icon: Icon( - FontAwesomeIcons.chevronRight, - color: colorScheme.surface, - size: 32, - ), + icon: Icon(FontAwesomeIcons.chevronRight, color: colorScheme.surface, size: 32), onPressed: _nextImage, - style: IconButton.styleFrom( - backgroundColor: Colors.white.withAlpha(51), - ), + style: IconButton.styleFrom(backgroundColor: Colors.white.withAlpha(51)), ), ), ), diff --git a/lib/features/projects/presentation/pages/submission_create_page.dart b/lib/features/projects/presentation/pages/submission_create_page.dart index b0195a2..2cf54f8 100644 --- a/lib/features/projects/presentation/pages/submission_create_page.dart +++ b/lib/features/projects/presentation/pages/submission_create_page.dart @@ -1166,13 +1166,13 @@ class _SubmissionCreatePageState extends ConsumerState { height: 24, child: CustomLoadingIndicator(color: Colors.white, size: 20), ) - : const Row( + : Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - FaIcon(FontAwesomeIcons.paperPlane, size: 16), - SizedBox(width: 8), + const FaIcon(FontAwesomeIcons.paperPlane, size: 16), + const SizedBox(width: 8), Text( - 'Gửi đăng ký', + isEditing ? 'Cập nhật' : 'Gửi đăng ký', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, diff --git a/pubspec.yaml b/pubspec.yaml index 9bb2dcb..ad3c443 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 1.0.1+24 +version: 1.0.1+26 environment: sdk: ^3.10.0