fix homepage news, invoice

This commit is contained in:
Phuoc Nguyen
2025-12-09 11:17:32 +07:00
parent e0a9b3b9f4
commit 597c6a0e57
8 changed files with 51 additions and 99 deletions

View File

@@ -131,7 +131,8 @@ class _HomePageState extends ConsumerState<HomePage> {
promotions: promotions, promotions: promotions,
onPromotionTap: (promotion) { onPromotionTap: (promotion) {
// Navigate to promotion details // Navigate to promotion details
context.push('/promotions/${promotion.id}'); context.push('/news/${promotion.id}');
}, },
) )
: const SizedBox.shrink(), : const SizedBox.shrink(),

View File

@@ -87,6 +87,7 @@ class BuyerInfoModel {
final String? wardCode; final String? wardCode;
final String? cityName; final String? cityName;
final String? wardName; final String? wardName;
final String? customerName;
const BuyerInfoModel({ const BuyerInfoModel({
this.name, this.name,
@@ -100,6 +101,7 @@ class BuyerInfoModel {
this.wardCode, this.wardCode,
this.cityName, this.cityName,
this.wardName, this.wardName,
this.customerName,
}); });
factory BuyerInfoModel.fromJson(Map<String, dynamic> json) { factory BuyerInfoModel.fromJson(Map<String, dynamic> json) {
@@ -115,6 +117,7 @@ class BuyerInfoModel {
wardCode: json['ward_code'] as String?, wardCode: json['ward_code'] as String?,
cityName: json['city_name'] as String?, cityName: json['city_name'] as String?,
wardName: json['ward_name'] as String?, wardName: json['ward_name'] as String?,
customerName: json['customer_name'] as String?,
); );
} }
@@ -130,6 +133,7 @@ class BuyerInfoModel {
'ward_code': wardCode, 'ward_code': wardCode,
'city_name': cityName, 'city_name': cityName,
'ward_name': wardName, 'ward_name': wardName,
'customer_name': customerName,
}; };
BuyerInfo toEntity() => BuyerInfo( BuyerInfo toEntity() => BuyerInfo(
@@ -144,6 +148,7 @@ class BuyerInfoModel {
wardCode: wardCode, wardCode: wardCode,
cityName: cityName, cityName: cityName,
wardName: wardName, wardName: wardName,
customerName: customerName,
); );
} }

View File

@@ -75,6 +75,7 @@ class BuyerInfo extends Equatable {
final String? wardCode; final String? wardCode;
final String? cityName; final String? cityName;
final String? wardName; final String? wardName;
final String? customerName;
const BuyerInfo({ const BuyerInfo({
this.name, this.name,
@@ -88,6 +89,7 @@ class BuyerInfo extends Equatable {
this.wardCode, this.wardCode,
this.cityName, this.cityName,
this.wardName, this.wardName,
this.customerName
}); });
/// Get formatted full address /// Get formatted full address
@@ -118,6 +120,7 @@ class BuyerInfo extends Equatable {
wardCode, wardCode,
cityName, cityName,
wardName, wardName,
customerName
]; ];
} }

View File

@@ -294,10 +294,10 @@ class InvoiceDetailPage extends ConsumerWidget {
List<_InfoLine> _buildBuyerInfoLines(Invoice invoice) { List<_InfoLine> _buildBuyerInfoLines(Invoice invoice) {
return [ return [
if (invoice.buyerInfo!.name != null) if (invoice.buyerInfo!.customerName != null)
_InfoLine(label: 'Người mua hàng', value: invoice.buyerInfo!.name!), _InfoLine(label: 'Người mua hàng', value: invoice.buyerInfo!.customerName!),
if (invoice.customerName != null) if (invoice.buyerInfo!.addressTitle != null)
_InfoLine(label: 'Tên đơn vị', value: invoice.customerName!), _InfoLine(label: 'Tên đơn vị', value: invoice.buyerInfo!.addressTitle!),
if (invoice.buyerInfo!.taxCode != null) if (invoice.buyerInfo!.taxCode != null)
_InfoLine(label: 'Mã số thuế', value: invoice.buyerInfo!.taxCode!), _InfoLine(label: 'Mã số thuế', value: invoice.buyerInfo!.taxCode!),
if (invoice.buyerInfo!.fullAddress.isNotEmpty) if (invoice.buyerInfo!.fullAddress.isNotEmpty)

View File

@@ -9,6 +9,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:shimmer/shimmer.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/constants/ui_constants.dart';
import 'package:worker/core/theme/colors.dart'; import 'package:worker/core/theme/colors.dart';
import 'package:worker/features/products/domain/entities/product.dart'; import 'package:worker/features/products/domain/entities/product.dart';
@@ -273,17 +274,14 @@ class ProductCard extends ConsumerWidget {
width: double.infinity, width: double.infinity,
height: 36.0, height: 36.0,
child: OutlinedButton.icon( child: OutlinedButton.icon(
onPressed: () { onPressed: () async {
// TODO: Open 360 view in browser final url = Uri.parse(product.customLink360!);
// For now, show a message if (await canLaunchUrl(url)) {
ScaffoldMessenger.of(context).showSnackBar( await launchUrl(
const SnackBar( url,
content: Text( mode: LaunchMode.inAppWebView,
'Đang phát triển tính năng xem 360°', );
), }
duration: Duration(seconds: 2),
),
);
}, },
style: OutlinedButton.styleFrom( style: OutlinedButton.styleFrom(
foregroundColor: colorScheme.primary, foregroundColor: colorScheme.primary,

View File

@@ -7,6 +7,7 @@ import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:shimmer/shimmer.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/constants/ui_constants.dart';
import 'package:worker/features/products/domain/entities/product.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) /// - Image indicators (dots)
/// - Thumbnail gallery row (horizontal scroll) /// - Thumbnail gallery row (horizontal scroll)
class ImageGallerySection extends StatefulWidget { class ImageGallerySection extends StatefulWidget {
const ImageGallerySection({super.key, required this.product}); const ImageGallerySection({super.key, required this.product});
final Product product; final Product product;
@@ -40,22 +40,15 @@ class _ImageGallerySectionState extends State<ImageGallerySection> {
setState(() { setState(() {
_currentImageIndex = index; _currentImageIndex = index;
}); });
_pageController.animateToPage( _pageController.animateToPage(index, duration: AppDuration.medium, curve: Curves.easeInOut);
index,
duration: AppDuration.medium,
curve: Curves.easeInOut,
);
} }
void _open360View() { void _open360View() async {
if (widget.product.customLink360 != null) { if (widget.product.customLink360 != null) {
// TODO: Open in browser final url = Uri.parse(widget.product.customLink360!);
ScaffoldMessenger.of(context).showSnackBar( if (await canLaunchUrl(url)) {
const SnackBar( await launchUrl(url, mode: LaunchMode.inAppWebView);
content: Text('Đang phát triển tính năng xem 360°'), }
duration: Duration(seconds: 2),
),
);
} }
} }
@@ -108,11 +101,7 @@ class _ImageGallerySectionState extends State<ImageGallerySection> {
), ),
errorWidget: (context, url, error) => Container( errorWidget: (context, url, error) => Container(
color: colorScheme.surfaceContainerHighest, color: colorScheme.surfaceContainerHighest,
child: Icon( child: Icon(FontAwesomeIcons.image, size: 64, color: colorScheme.onSurfaceVariant),
FontAwesomeIcons.image,
size: 64,
color: colorScheme.onSurfaceVariant,
),
), ),
), ),
); );
@@ -131,10 +120,7 @@ class _ImageGallerySectionState extends State<ImageGallerySection> {
onTap: _open360View, onTap: _open360View,
borderRadius: BorderRadius.circular(20), borderRadius: BorderRadius.circular(20),
child: Container( child: Container(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
horizontal: 12,
vertical: 8,
),
child: Row( child: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
@@ -144,11 +130,7 @@ class _ImageGallerySectionState extends State<ImageGallerySection> {
builder: (context, double value, child) { builder: (context, double value, child) {
return Transform.rotate( return Transform.rotate(
angle: value * 2 * 3.14159, angle: value * 2 * 3.14159,
child: Icon( child: Icon(FontAwesomeIcons.arrowsRotate, size: 10, color: colorScheme.surface),
FontAwesomeIcons.arrowsRotate,
size: 10,
color: colorScheme.surface,
),
); );
}, },
onEnd: () { onEnd: () {
@@ -159,11 +141,7 @@ class _ImageGallerySectionState extends State<ImageGallerySection> {
const SizedBox(width: 6), const SizedBox(width: 6),
Text( Text(
'360°', '360°',
style: TextStyle( style: TextStyle(color: colorScheme.surface, fontSize: 12, fontWeight: FontWeight.w600),
color: colorScheme.surface,
fontSize: 12,
fontWeight: FontWeight.w600,
),
), ),
], ],
), ),
@@ -245,9 +223,7 @@ class _ImageGallerySectionState extends State<ImageGallerySection> {
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
border: Border.all( border: Border.all(
color: isActive color: isActive ? colorScheme.primary : Colors.transparent,
? colorScheme.primary
: Colors.transparent,
width: 2, width: 2,
), ),
), ),
@@ -256,15 +232,10 @@ class _ImageGallerySectionState extends State<ImageGallerySection> {
child: CachedNetworkImage( child: CachedNetworkImage(
imageUrl: imageUrl, imageUrl: imageUrl,
fit: BoxFit.cover, fit: BoxFit.cover,
placeholder: (context, url) => placeholder: (context, url) => Container(color: colorScheme.surfaceContainerHighest),
Container(color: colorScheme.surfaceContainerHighest),
errorWidget: (context, url, error) => Container( errorWidget: (context, url, error) => Container(
color: colorScheme.surfaceContainerHighest, color: colorScheme.surfaceContainerHighest,
child: Icon( child: Icon(FontAwesomeIcons.image, size: 20, color: colorScheme.onSurfaceVariant),
FontAwesomeIcons.image,
size: 20,
color: colorScheme.onSurfaceVariant,
),
), ),
), ),
), ),
@@ -285,12 +256,7 @@ class _ImageGallerySectionState extends State<ImageGallerySection> {
/// Image Lightbox for full-screen image viewing /// Image Lightbox for full-screen image viewing
class _ImageLightbox extends StatefulWidget { 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<String> images; final List<String> images;
final Map<String, String> imageCaptions; final Map<String, String> imageCaptions;
final int initialIndex; final int initialIndex;
@@ -318,19 +284,13 @@ class _ImageLightboxState extends State<_ImageLightbox> {
void _nextImage() { void _nextImage() {
if (_currentIndex < widget.images.length - 1) { if (_currentIndex < widget.images.length - 1) {
_pageController.nextPage( _pageController.nextPage(duration: AppDuration.medium, curve: Curves.easeInOut);
duration: AppDuration.medium,
curve: Curves.easeInOut,
);
} }
} }
void _previousImage() { void _previousImage() {
if (_currentIndex > 0) { if (_currentIndex > 0) {
_pageController.previousPage( _pageController.previousPage(duration: AppDuration.medium, curve: Curves.easeInOut);
duration: AppDuration.medium,
curve: Curves.easeInOut,
);
} }
} }
@@ -372,11 +332,8 @@ class _ImageLightboxState extends State<_ImageLightbox> {
child: CachedNetworkImage( child: CachedNetworkImage(
imageUrl: widget.images[index], imageUrl: widget.images[index],
fit: BoxFit.contain, fit: BoxFit.contain,
errorWidget: (context, url, error) => Icon( errorWidget: (context, url, error) =>
FontAwesomeIcons.circleExclamation, Icon(FontAwesomeIcons.circleExclamation, color: colorScheme.surface, size: 64),
color: colorScheme.surface,
size: 64,
),
), ),
), ),
); );
@@ -393,15 +350,9 @@ class _ImageLightboxState extends State<_ImageLightbox> {
bottom: 0, bottom: 0,
child: Center( child: Center(
child: IconButton( child: IconButton(
icon: Icon( icon: Icon(FontAwesomeIcons.chevronLeft, color: colorScheme.surface, size: 32),
FontAwesomeIcons.chevronLeft,
color: colorScheme.surface,
size: 32,
),
onPressed: _previousImage, onPressed: _previousImage,
style: IconButton.styleFrom( style: IconButton.styleFrom(backgroundColor: Colors.white.withAlpha(51)),
backgroundColor: Colors.white.withAlpha(51),
),
), ),
), ),
), ),
@@ -414,15 +365,9 @@ class _ImageLightboxState extends State<_ImageLightbox> {
bottom: 0, bottom: 0,
child: Center( child: Center(
child: IconButton( child: IconButton(
icon: Icon( icon: Icon(FontAwesomeIcons.chevronRight, color: colorScheme.surface, size: 32),
FontAwesomeIcons.chevronRight,
color: colorScheme.surface,
size: 32,
),
onPressed: _nextImage, onPressed: _nextImage,
style: IconButton.styleFrom( style: IconButton.styleFrom(backgroundColor: Colors.white.withAlpha(51)),
backgroundColor: Colors.white.withAlpha(51),
),
), ),
), ),
), ),

View File

@@ -1166,13 +1166,13 @@ class _SubmissionCreatePageState extends ConsumerState<SubmissionCreatePage> {
height: 24, height: 24,
child: CustomLoadingIndicator(color: Colors.white, size: 20), child: CustomLoadingIndicator(color: Colors.white, size: 20),
) )
: const Row( : Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
FaIcon(FontAwesomeIcons.paperPlane, size: 16), const FaIcon(FontAwesomeIcons.paperPlane, size: 16),
SizedBox(width: 8), const SizedBox(width: 8),
Text( Text(
'Gửi đăng ký', isEditing ? 'Cập nhật' : 'Gửi đăng ký',
style: TextStyle( style: TextStyle(
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,

View File

@@ -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 # 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 # 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. # 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: environment:
sdk: ^3.10.0 sdk: ^3.10.0