Commit f0334970 authored by DatHV's avatar DatHV
Browse files

update flash sale

parent b93b2948
import 'package:json_annotation/json_annotation.dart';
import '../../flash_sale/preview_flash_sale_model.dart';
import '../../flash_sale/models/preview_flash_sale_model.dart';
import '../../voucher/models/product_model.dart';
part 'flash_sale_model.g.dart';
......
import 'package:json_annotation/json_annotation.dart';
import 'package:mypoint_flutter_app/directional/directional_screen.dart';
import '../../flash_sale/preview_flash_sale_model.dart';
import '../../flash_sale/models/preview_flash_sale_model.dart';
import 'header_section_type.dart';
part 'main_section_config_model.g.dart';
......
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:mypoint_flutter_app/extensions/string_extension.dart';
import 'package:mypoint_flutter_app/widgets/custom_app_bar.dart';
import '../../base/base_screen.dart';
import '../../base/basic_state.dart';
import '../../widgets/image_loader.dart';
import 'models/notification_detail_model.dart';
import 'notification_detail_viewmodel.dart';
class NotificationDetailScreen extends StatelessWidget {
final NotificationDetailModel notification;
class NotificationDetailScreen extends BaseScreen {
const NotificationDetailScreen({super.key});
const NotificationDetailScreen({super.key, required this.notification});
@override
State<NotificationDetailScreen> createState() => _NotificationDetailScreenState();
}
class _NotificationDetailScreenState extends BaseState<NotificationDetailScreen> with BasicState {
final _viewModel = Get.put(NotificationDetailViewModel());
@override
void initState() {
super.initState();
NotificationDetailModel? notification;
String? notificationId;
String? title;
String? body;
final args = Get.arguments;
if (args is Map) {
notification = args['notification'];
notificationId = args['notificationId'];
title = args['title'];
body = args['body'];
}
if (title.orEmpty.isNotEmpty && body.orEmpty.isNotEmpty) {
notification ??= NotificationDetailModel(title: title, body: body);
}
if (notificationId == null && notification == null) {
WidgetsBinding.instance.addPostFrameCallback((_) {
Get.back();
});
return;
}
_viewModel.onShowAlertError = (message) {
if (message.isEmpty) return;
showAlertError(
content: message,
onConfirmed: () {
Get.back();
},
);
};
_viewModel.fetchNotificationDetail(id: notificationId, data: notification);
}
@override
Widget build(BuildContext context) {
Widget createBody() {
return Scaffold(
appBar: CustomAppBar.back(title: "Chi tiết thông báo"),
body: Container(
color: Colors.grey.shade100,
padding: const EdgeInsets.all(16),
child: IntrinsicHeight(
child: Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(12)),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ClipRRect(
borderRadius: BorderRadius.circular(8),
child: loadNetworkImage(
url: notification.workingSite?.avatar ?? "",
fit: BoxFit.cover,
width: 40,
height: 40,
placeholderAsset: 'assets/images/ic_logo.png',
body: Obx(
() => Container(
color: Colors.grey.shade100,
padding: const EdgeInsets.all(16),
child: IntrinsicHeight(
child: Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(12)),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ClipRRect(
borderRadius: BorderRadius.circular(8),
child: loadNetworkImage(
url: _viewModel.notification.value?.workingSite?.avatar ?? "",
fit: BoxFit.cover,
width: 40,
height: 40,
placeholderAsset: 'assets/images/ic_logo.png',
),
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(notification.title ?? '', style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16)),
const SizedBox(height: 6),
Text(notification.body ?? '', style: const TextStyle(fontSize: 14)),
const SizedBox(height: 10),
Text(_timeAgo(notification.createTime), style: const TextStyle(color: Colors.grey, fontSize: 13)),
],
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
_viewModel.notification.value?.title ?? '',
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
),
const SizedBox(height: 6),
Text(_viewModel.notification.value?.body ?? '', style: const TextStyle(fontSize: 14)),
const SizedBox(height: 10),
if (_viewModel.notification.value?.createTime.orEmpty.isNotEmpty == true)
Text(
_timeAgo(_viewModel.notification.value?.createTime),
style: const TextStyle(color: Colors.grey, fontSize: 13),
),
],
),
),
),
],
],
),
),
),
),
......
import 'package:get/get_rx/src/rx_types/rx_types.dart';
import 'package:mypoint_flutter_app/networking/restful_api_client_all_request.dart';
import '../../configs/constants.dart';
import '../../networking/restful_api_viewmodel.dart';
import 'models/notification_detail_model.dart';
class NotificationDetailViewModel extends RestfulApiViewModel {
var notification = Rxn<NotificationDetailModel>();
void Function(String message)? onShowAlertError;
Future<void> fetchNotificationDetail({String? id, NotificationDetailModel? data}) async {
if (data != null) {
notification.value = data;
return;
}
if (id == null) return;
await callApi<NotificationDetailResponseModel>(
request: () => client.getNotificationDetail(id ?? ''),
onSuccess: (data, _) {
final notify = data.notification;
if (notify != null) {
notification.value = data.notification;
} else {
onShowAlertError?.call(Constants.commonError);
}
},
onFailure: (msg, _, _) async {
onShowAlertError?.call(msg);
},
);
}
}
\ No newline at end of file
......@@ -3,6 +3,7 @@ import 'package:mypoint_flutter_app/networking/restful_api_client_all_request.da
import '../../base/base_response_model.dart';
import '../../networking/restful_api_viewmodel.dart';
import '../../preference/data_preference.dart';
import '../../shared/router_gage.dart';
import 'models/category_notify_item_model.dart';
import 'models/notification_detail_model.dart';
import 'models/notification_item_model.dart';
......@@ -128,7 +129,7 @@ class NotificationViewModel extends RestfulApiViewModel {
if (notification == null) return;
final pushSuccess = notification.directionalScreen?.begin() ?? false;
if (!pushSuccess) {
Get.to(() => NotificationDetailScreen(notification: notification));
Get.toNamed(notificationDetailScreen, arguments: {'notification': notification, 'notificationId': item.notificationId});
}
},
onFailure: (msg, _, _) async {
......
import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:get/get.dart';
import 'package:mypoint_flutter_app/extensions/string_extension.dart';
......
import 'dart:core';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
import 'package:mypoint_flutter_app/extensions/num_extension.dart';
import '../../base/base_screen.dart';
import '../../base/basic_state.dart';
import '../../resources/base_color.dart';
......@@ -22,7 +22,6 @@ class TransactionDetailScreen extends BaseScreen {
class _TransactionDetailScreenState extends BaseState<TransactionDetailScreen> with BasicState {
late final TransactionDetailViewModel _viewModel;
final currencyFormatter = NumberFormat.currency(locale: 'vi_VN', symbol: 'đ', decimalDigits: 0);
ProductModel? _product;
String? _metaData;
int _quantity = 1;
......@@ -211,9 +210,6 @@ class _TransactionDetailScreenState extends BaseState<TransactionDetailScreen> w
}
Widget _buildTotalRow(String label, int amount, bool isHighlighted) {
final formattedAmount = currencyFormatter.format(amount);
final displayAmount = amount < 0 ? formattedAmount : formattedAmount;
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Row(
......@@ -221,7 +217,7 @@ class _TransactionDetailScreenState extends BaseState<TransactionDetailScreen> w
children: [
Text(label, style: TextStyle(fontSize: 16, color: Colors.grey.shade700)),
Text(
displayAmount,
amount.money(CurrencyUnit.vnd),
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
......@@ -469,7 +465,7 @@ class _TransactionDetailScreenState extends BaseState<TransactionDetailScreen> w
const Text('Tổng thanh toán', style: TextStyle(fontSize: 14, color: Colors.grey)),
const SizedBox(height: 4),
Text(
currencyFormatter.format(_viewModel.finalTotal),
_viewModel.finalTotal.money(CurrencyUnit.vnd),
style: const TextStyle(fontSize: 22, fontWeight: FontWeight.bold),
),
],
......
......@@ -159,6 +159,7 @@ class _VoucherDetailScreenState extends BaseState<VoucherDetailScreen> with Basi
}
Widget _buildInfo(ProductModel product, {Key? key}) {
final double screenWidth = MediaQuery.of(context).size.width;
return Container(
key: key,
padding: const EdgeInsets.all(16),
......@@ -198,9 +199,59 @@ class _VoucherDetailScreenState extends BaseState<VoucherDetailScreen> with Basi
const SizedBox(width: 8),
Expanded(child: Text(product.brand?.name ?? '', style: const TextStyle(fontSize: 14))),
// PriceTagWidget(point: product.amountToBePaid ?? 0),
if (product.percentDiscount != null)
Container(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
decoration: BoxDecoration(color: Colors.red, borderRadius: BorderRadius.circular(20)),
child: Text(
'-${product.percentDiscount}%',
style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 12),
),
),
const SizedBox(width: 4),
CustomPointText(point: product.amountToBePaid ?? 0, type: product.price?.method),
const SizedBox(width: 4),
if (product.price?.value != null && product.previewFlashSale?.isFlashSalePrice == true)
Text(
'${product.price?.value?.money(CurrencyUnit.noneSpace)}đ',
style: const TextStyle(
fontSize: 14,
color: Colors.grey,
decoration: TextDecoration.lineThrough,
),
),
],
),
if (product.isShowProsessSoldItem)
const SizedBox(height: 16),
if (product.isShowProsessSoldItem)
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// Thanh tiến trình
SizedBox(
width: screenWidth - 64 - 90,
child: ClipRRect(
borderRadius: BorderRadius.circular(6),
child: LinearProgressIndicator(
value: product.progress,
minHeight: 10,
backgroundColor: Colors.grey.shade300,
valueColor: AlwaysStoppedAnimation<Color>(
product.inStock ? Colors.orange : Colors.red,
),
),
),
),
const SizedBox(width: 2),
Text(
product.inStock
? 'Đã bán ${product.previewFlashSale?.fsQuantitySold ?? 0}'
: 'Đã bán hết',
style: TextStyle(fontSize: 12, color: product.inStock ? Colors.black : Colors.grey),
),
],
),
],
),
);
......@@ -218,7 +269,7 @@ class _VoucherDetailScreenState extends BaseState<VoucherDetailScreen> with Basi
if (hasExpire)
Text(
product.expired ? "Hết hạn" : product.expire,
style: const TextStyle(color: BaseColor.primary500, fontWeight: FontWeight.bold, fontSize: 12),
style: const TextStyle(color: Colors.orange, fontWeight: FontWeight.bold, fontSize: 14),
),
if (isOutOfStock && !_viewModel.isMyProduct)
Container(
......
......@@ -11,7 +11,7 @@ import 'package:mypoint_flutter_app/screen/voucher/models/product_price_model.da
import 'package:mypoint_flutter_app/screen/voucher/models/product_properties_model.dart';
import 'package:mypoint_flutter_app/screen/voucher/models/product_type.dart';
import 'package:mypoint_flutter_app/widgets/alert/popup_data_model.dart';
import '../../flash_sale/preview_flash_sale_model.dart';
import '../../flash_sale/models/preview_flash_sale_model.dart';
import 'media_type.dart';
import 'my_product_status_type.dart';
......
......@@ -14,6 +14,7 @@ import '../screen/data_network_service/data_network_service_screen.dart';
import '../screen/device_manager/device_manager_screen.dart';
import '../screen/electric_payment/electric_payment_history_screen.dart';
import '../screen/electric_payment/electric_payment_screen.dart';
import '../screen/flash_sale/flash_sale_screen.dart';
import '../screen/game/game_cards/game_card_screen.dart';
import '../screen/game/game_tab_screen.dart';
import '../screen/health_book/health_book_card_detail.dart';
......@@ -27,6 +28,7 @@ import '../screen/login/login_screen.dart';
import '../screen/main_tab_screen/main_tab_screen.dart';
import '../screen/membership/membership_screen.dart';
import '../screen/mobile_card/product_mobile_card_screen.dart';
import '../screen/notification/notification_detail_screen.dart';
import '../screen/notification/notification_screen.dart';
import '../screen/onboarding/onboarding_screen.dart';
import '../screen/order_menu/order_menu_screen.dart';
......@@ -56,6 +58,7 @@ const onboardingScreen = '/onboarding';
const loginScreen = '/login';
const mainScreen = '/main';
const settingScreen = '/setting';
const flashSaleScreen = '/flashSale';
const vouchersScreen = '/vouchers';
const voucherDetailScreen = '/voucherDetail';
const gameCardScreen = '/gameCardScreen';
......@@ -103,6 +106,7 @@ const historyPointScreen = '/historyPointScreen';
const qrCodeScreen = '/qrCodeScreen';
const myMobileCardDetailScreen = '/myMobileCardDetailScreen';
const healthBookCardDetail = '/healthBookCardDetail';
const notificationDetailScreen = '/notificationDetailScreen';
class RouterPage {
static List<GetPage> pages() {
......@@ -125,6 +129,7 @@ class RouterPage {
),
GetPage(name: settingScreen, page: () => SettingScreen()),
GetPage(name: vouchersScreen, page: () => VoucherListScreen()),
GetPage(name: flashSaleScreen, page: () => const FlashSaleScreen()),
GetPage(name: voucherDetailScreen, page: () => VoucherDetailScreen()),
GetPage(name: gameCardScreen, page: () => GameCardScreen()),
GetPage(name: registerFormInputScreen, page: () => RegisterFormInputScreen()),
......@@ -171,6 +176,7 @@ class RouterPage {
GetPage(name: myMobileCardDetailScreen, page: () => MyMobileCardDetailScreen()),
GetPage(name: healthBookScreen, page: () => HealthBookScreen()),
GetPage(name: healthBookCardDetail, page: () => HealthBookCardDetail()),
GetPage(name: notificationDetailScreen, page: () => NotificationDetailScreen()),
];
}
}
\ No newline at end of file
......@@ -25,7 +25,7 @@ class CustomPointText extends StatelessWidget {
Text(
isFree ? 'Miễn phí' : point.money(currencyUnit),
style: TextStyle(
fontSize: 12,
fontSize: 14,
color: Colors.black,
fontWeight: FontWeight.bold,
),
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment