Commit efb4662c authored by DatHV's avatar DatHV
Browse files

update campaign 7day

parent 4c376d38
...@@ -65,7 +65,7 @@ class VoucherListItem extends StatelessWidget { ...@@ -65,7 +65,7 @@ class VoucherListItem extends StatelessWidget {
if (!product.inStock) if (!product.inStock)
Positioned.fill( Positioned.fill(
child: Container( child: Container(
color: Colors.black.withOpacity(0.5), color: Colors.black.withOpacity(0.3),
alignment: Alignment.center, alignment: Alignment.center,
child: const Text( child: const Text(
'Tạm hết', 'Tạm hết',
......
...@@ -19,6 +19,7 @@ class _VoucherListScreenState extends State<VoucherListScreen> { ...@@ -19,6 +19,7 @@ class _VoucherListScreenState extends State<VoucherListScreen> {
late final Map<String, dynamic> args; late final Map<String, dynamic> args;
late final bool enableSearch; late final bool enableSearch;
late final bool isHotProduct; late final bool isHotProduct;
late final bool isFavorite;
late final VoucherListViewModel _viewModel; late final VoucherListViewModel _viewModel;
@override @override
...@@ -27,12 +28,13 @@ class _VoucherListScreenState extends State<VoucherListScreen> { ...@@ -27,12 +28,13 @@ class _VoucherListScreenState extends State<VoucherListScreen> {
args = Get.arguments ?? {}; args = Get.arguments ?? {};
enableSearch = args['enableSearch'] ?? false; enableSearch = args['enableSearch'] ?? false;
isHotProduct = args['isHotProduct'] ?? false; isHotProduct = args['isHotProduct'] ?? false;
_viewModel = Get.put(VoucherListViewModel(isHotProduct: isHotProduct)); isFavorite = args['favorite'] ?? false;
_viewModel = Get.put(VoucherListViewModel(isHotProduct: isHotProduct, isFavorite: isFavorite));
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final String title = isHotProduct ? 'Săn ưu đãi' : 'Tất cả ưu đãi'; final String title = isFavorite ? 'Yêu thích' : (isHotProduct ? 'Săn ưu đãi' : 'Tất cả ưu đãi');
return Scaffold( return Scaffold(
appBar: appBar:
enableSearch enableSearch
...@@ -69,21 +71,22 @@ class _VoucherListScreenState extends State<VoucherListScreen> { ...@@ -69,21 +71,22 @@ class _VoucherListScreenState extends State<VoucherListScreen> {
); );
} }
return RefreshIndicator( return RefreshIndicator(
onRefresh: () => _viewModel.getProducts(reset: true), onRefresh: () => _viewModel.loadData(reset: true),
child: ListView.builder( child: ListView.builder(
physics: const AlwaysScrollableScrollPhysics(), physics: const AlwaysScrollableScrollPhysics(),
itemCount: _viewModel.products.length + (_viewModel.hasMore ? 1 : 0), itemCount: _viewModel.products.length + (_viewModel.hasMore ? 1 : 0),
itemBuilder: (context, index) { itemBuilder: (context, index) {
if (index >= _viewModel.products.length) { if (index >= _viewModel.products.length) {
_viewModel.getProducts(reset: false); _viewModel.loadData(reset: false);
return const Center( return const Center(
child: Padding(padding: EdgeInsets.all(16), child: CircularProgressIndicator()), child: Padding(padding: EdgeInsets.all(16), child: CircularProgressIndicator()),
); );
} }
final product = _viewModel.products[index]; final product = _viewModel.products[index];
return GestureDetector( return GestureDetector(
onTap: () { onTap: () async {
Get.toNamed(voucherDetailScreen, arguments: {"productId": product.id}); await Get.toNamed(voucherDetailScreen, arguments: {"productId": product.id});
_viewModel.loadData(reset: true);
}, },
child: VoucherListItem(product: product), child: VoucherListItem(product: product),
); );
......
...@@ -6,8 +6,8 @@ import '../models/product_model.dart'; ...@@ -6,8 +6,8 @@ import '../models/product_model.dart';
import '../models/product_type.dart'; import '../models/product_type.dart';
class VoucherListViewModel extends RestfulApiViewModel { class VoucherListViewModel extends RestfulApiViewModel {
VoucherListViewModel({required this.isHotProduct}); VoucherListViewModel({required this.isHotProduct, this.isFavorite = false});
final bool isFavorite;
final bool isHotProduct; final bool isHotProduct;
Timer? _debounce; Timer? _debounce;
var products = <ProductModel>[].obs; var products = <ProductModel>[].obs;
...@@ -24,7 +24,7 @@ class VoucherListViewModel extends RestfulApiViewModel { ...@@ -24,7 +24,7 @@ class VoucherListViewModel extends RestfulApiViewModel {
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
getProducts(reset: true); loadData(reset: true);
} }
@override @override
...@@ -38,11 +38,47 @@ class VoucherListViewModel extends RestfulApiViewModel { ...@@ -38,11 +38,47 @@ class VoucherListViewModel extends RestfulApiViewModel {
_searchQuery = value; _searchQuery = value;
_debounce?.cancel(); _debounce?.cancel();
_debounce = Timer(const Duration(seconds: 1), () { _debounce = Timer(const Duration(seconds: 1), () {
getProducts(reset: true); loadData(reset: true);
}); });
} }
Future<void> getProducts({bool reset = false}) async { Future<void> loadData({bool reset = false}) async {
if (isFavorite) {
await _getFavoriteProducts(reset: reset);
} else {
await _getProducts(reset: reset);
}
}
Future<void> _getFavoriteProducts({bool reset = false}) async {
if (isLoading.value) return;
if (reset) {
_currentPage = 0;
_hasMore = true;
products.clear();
} else {
_currentPage = products.length;
}
if (!_hasMore) return;
final body = {"size": _pageSize, "index": _currentPage};
try {
isLoading.value = true;
isLoadMore.value = true;
final result = await client.productsCustomerLikes(body);
final fetchedData = result.data ?? [];
if (fetchedData.isEmpty || fetchedData.length < _pageSize) {
_hasMore = false;
}
products.addAll(fetchedData);
} catch (error) {
print("Error fetching products: $error");
} finally {
isLoading.value = false;
isLoadMore.value = false;
}
}
Future<void> _getProducts({bool reset = false}) async {
if (isLoading.value) return; if (isLoading.value) return;
if (reset) { if (reset) {
_currentPage = 0; _currentPage = 0;
......
...@@ -41,6 +41,7 @@ class _BaseWebViewScreenState extends BaseState<BaseWebViewScreen> with BasicSta ...@@ -41,6 +41,7 @@ class _BaseWebViewScreenState extends BaseState<BaseWebViewScreen> with BasicSta
}); });
return; return;
} }
showLoading();
_controller = _controller =
WebViewController() WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted) ..setJavaScriptMode(JavaScriptMode.unrestricted)
...@@ -48,12 +49,14 @@ class _BaseWebViewScreenState extends BaseState<BaseWebViewScreen> with BasicSta ...@@ -48,12 +49,14 @@ class _BaseWebViewScreenState extends BaseState<BaseWebViewScreen> with BasicSta
..setNavigationDelegate( ..setNavigationDelegate(
NavigationDelegate( NavigationDelegate(
onPageFinished: (_) async { onPageFinished: (_) async {
hideLoading();
final title = await _controller.getTitle(); final title = await _controller.getTitle();
setState(() { setState(() {
_dynamicTitle = title; _dynamicTitle = title;
}); });
}, },
onWebResourceError: (error) { onWebResourceError: (error) {
hideLoading();
if (error.description != 'about:blank') { if (error.description != 'about:blank') {
showAlertError(content: error.description); showAlertError(content: error.description);
} }
......
...@@ -6,9 +6,12 @@ import '../screen/affiliate/affiliate_tab_screen.dart'; ...@@ -6,9 +6,12 @@ import '../screen/affiliate/affiliate_tab_screen.dart';
import '../screen/affiliate_brand_detail/affiliate_brand_detail_screen.dart'; import '../screen/affiliate_brand_detail/affiliate_brand_detail_screen.dart';
import '../screen/affiliate_brand_detail/affiliate_brand_list_screen.dart'; import '../screen/affiliate_brand_detail/affiliate_brand_list_screen.dart';
import '../screen/affiliate_brand_detail/affiliate_category_grid_screen.dart'; import '../screen/affiliate_brand_detail/affiliate_category_grid_screen.dart';
import '../screen/campaign7day/campaign_7day_screen.dart';
import '../screen/contacts/contacts_list_screen.dart'; import '../screen/contacts/contacts_list_screen.dart';
import '../screen/daily_checkin/daily_checkin_screen.dart'; import '../screen/daily_checkin/daily_checkin_screen.dart';
import '../screen/data_network_service/data_network_service_screen.dart'; import '../screen/data_network_service/data_network_service_screen.dart';
import '../screen/electric_payment/electric_payment_history_screen.dart';
import '../screen/electric_payment/electric_payment_screen.dart';
import '../screen/game/game_cards/game_card_screen.dart'; import '../screen/game/game_cards/game_card_screen.dart';
import '../screen/game/game_tab_screen.dart'; import '../screen/game/game_tab_screen.dart';
import '../screen/history_point_cashback/history_point_cashback_screen.dart'; import '../screen/history_point_cashback/history_point_cashback_screen.dart';
...@@ -23,13 +26,17 @@ import '../screen/onboarding/onboarding_screen.dart'; ...@@ -23,13 +26,17 @@ import '../screen/onboarding/onboarding_screen.dart';
import '../screen/order_menu/order_menu_screen.dart'; import '../screen/order_menu/order_menu_screen.dart';
import '../screen/pageDetail/campaign_detail_screen.dart'; import '../screen/pageDetail/campaign_detail_screen.dart';
import '../screen/personal/personal_edit_screen.dart'; import '../screen/personal/personal_edit_screen.dart';
import '../screen/quiz_campaign/quiz_campaign_screen.dart';
import '../screen/register_campaign/register_form_input_screen.dart'; import '../screen/register_campaign/register_form_input_screen.dart';
import '../screen/setting/setting_screen.dart'; import '../screen/setting/setting_screen.dart';
import '../screen/splash/splash_screen.dart'; import '../screen/splash/splash_screen.dart';
import '../screen/support/support_screen.dart'; import '../screen/support/support_screen.dart';
import '../screen/topup/topup_screen.dart'; import '../screen/topup/topup_screen.dart';
import '../screen/traffic_service/traffic_service_detail_screen.dart';
import '../screen/traffic_service/traffic_service_screen.dart';
import '../screen/transaction/history/transaction_history_detail_screen.dart'; import '../screen/transaction/history/transaction_history_detail_screen.dart';
import '../screen/transaction/transaction_detail_screen.dart'; import '../screen/transaction/transaction_detail_screen.dart';
import '../screen/transaction/transactions_history_screen.dart';
import '../screen/voucher/detail/voucher_detail_screen.dart'; import '../screen/voucher/detail/voucher_detail_screen.dart';
import '../screen/voucher/my_voucher/my_product_list_widget.dart'; import '../screen/voucher/my_voucher/my_product_list_widget.dart';
import '../screen/voucher/voucher_list/voucher_list_screen.dart'; import '../screen/voucher/voucher_list/voucher_list_screen.dart';
...@@ -73,6 +80,13 @@ const affiliateCategoryGridScreen = '/affiliateCategoryGridScreen'; ...@@ -73,6 +80,13 @@ const affiliateCategoryGridScreen = '/affiliateCategoryGridScreen';
const inviteFriendCampaignScreen = '/inviteFriendCampaignScreen'; const inviteFriendCampaignScreen = '/inviteFriendCampaignScreen';
const contactsListScreen = '/contactsListScreen'; const contactsListScreen = '/contactsListScreen';
const dailyCheckInScreen = '/dailyCheckInScreen'; const dailyCheckInScreen = '/dailyCheckInScreen';
const transactionHistoryScreen = '/transactionHistoryScreen';
const electricPaymentScreen = '/electricPaymentScreen';
const electricPaymentHistoryScreen = '/electricPaymentHistoryScreen';
const trafficServiceScreen = '/trafficServiceScreen';
const trafficServiceDetailScreen = '/trafficServiceDetailScreen';
const campaignSevenDayScreen = '/campaignSevenDayScreen';
const surveyQuestionScreen = '/surveyQuestionScreen';
class RouterPage { class RouterPage {
static List<GetPage> pages() { static List<GetPage> pages() {
...@@ -86,7 +100,13 @@ class RouterPage { ...@@ -86,7 +100,13 @@ class RouterPage {
GetPage(name: splashScreen, page: () => SplashScreen()), GetPage(name: splashScreen, page: () => SplashScreen()),
GetPage(name: onboardingScreen, page: () => OnboardingScreen()), GetPage(name: onboardingScreen, page: () => OnboardingScreen()),
GetPage(name: loginScreen, page: () => LoginScreen()), GetPage(name: loginScreen, page: () => LoginScreen()),
GetPage(name: mainScreen, page: () => MainTabScreen(), customTransition: NoSwipeBackTransition()), GetPage(
name: mainScreen,
page: () => MainTabScreen(),
participatesInRootNavigator: true,
fullscreenDialog: true,
binding: BindingsBuilder(() {}),
),
GetPage(name: settingScreen, page: () => SettingScreen()), GetPage(name: settingScreen, page: () => SettingScreen()),
GetPage(name: vouchersScreen, page: () => VoucherListScreen()), GetPage(name: vouchersScreen, page: () => VoucherListScreen()),
GetPage(name: voucherDetailScreen, page: () => VoucherDetailScreen()), GetPage(name: voucherDetailScreen, page: () => VoucherDetailScreen()),
...@@ -119,20 +139,13 @@ class RouterPage { ...@@ -119,20 +139,13 @@ class RouterPage {
GetPage(name: inviteFriendCampaignScreen, page: () => InviteFriendCampaignScreen()), GetPage(name: inviteFriendCampaignScreen, page: () => InviteFriendCampaignScreen()),
GetPage(name: contactsListScreen, page: () => ContactsListScreen()), GetPage(name: contactsListScreen, page: () => ContactsListScreen()),
GetPage(name: dailyCheckInScreen, page: () => DailyCheckInScreen()), GetPage(name: dailyCheckInScreen, page: () => DailyCheckInScreen()),
GetPage(name: transactionHistoryScreen, page: () => TransactionHistoryScreen()),
GetPage(name: electricPaymentScreen, page: () => ElectricPaymentScreen()),
GetPage(name: electricPaymentHistoryScreen, page: () => ElectricPaymentHistoryScreen()),
GetPage(name: trafficServiceScreen, page: () => TrafficServiceScreen()),
GetPage(name: trafficServiceDetailScreen, page: () => TrafficServiceDetailScreen()),
GetPage(name: campaignSevenDayScreen, page: () => Campaign7DayScreen()),
GetPage(name: surveyQuestionScreen, page: () => SurveyQuestionScreen()),
]; ];
} }
}
class NoSwipeBackTransition extends CustomTransition {
@override
Widget buildTransition(
BuildContext context,
Curve? curve,
Alignment? alignment,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
) {
return child;
}
} }
\ No newline at end of file
...@@ -24,7 +24,7 @@ class ButtonConfigModel { ...@@ -24,7 +24,7 @@ class ButtonConfigModel {
final bgColor = color?.toColor() ?? Colors.white; final bgColor = color?.toColor() ?? Colors.white;
return AlertButton( return AlertButton(
text: text ?? "", text: text ?? "",
textColor: bgColor.invert, textColor: bgColor.contrastTextColor,
bgColor: bgColor, bgColor: bgColor,
onPressed: () async { onPressed: () async {
DirectionalScreen? directional = DirectionalScreen.build( DirectionalScreen? directional = DirectionalScreen.build(
......
This diff is collapsed.
...@@ -5,6 +5,9 @@ import 'package:get/get.dart'; ...@@ -5,6 +5,9 @@ import 'package:get/get.dart';
class BottomSheetHelper { class BottomSheetHelper {
static void showBottomSheetPopup({ static void showBottomSheetPopup({
required Widget child, required Widget child,
Color backgroundContainerColor = Colors.white,
double horizontalContainerPadding = 8.0,
double bottomContainerPadding = 16.0,
bool isDismissible = true, bool isDismissible = true,
}) { }) {
showModalBottomSheet( showModalBottomSheet(
...@@ -12,11 +15,12 @@ class BottomSheetHelper { ...@@ -12,11 +15,12 @@ class BottomSheetHelper {
isScrollControlled: true, isScrollControlled: true,
isDismissible: isDismissible, isDismissible: isDismissible,
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
barrierColor: Colors.black.withOpacity(0.5), barrierColor: Colors.black.withOpacity(0.7),
shape: const RoundedRectangleBorder( shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(16)), borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
), ),
builder: (context) { builder: (context) {
final bottom = MediaQuery.of(context).padding.bottom;
return Padding( return Padding(
padding: MediaQuery.of(context).viewInsets.add( padding: MediaQuery.of(context).viewInsets.add(
const EdgeInsets.only(bottom: 0), // 👈 Safe area bottom const EdgeInsets.only(bottom: 0), // 👈 Safe area bottom
...@@ -24,11 +28,15 @@ class BottomSheetHelper { ...@@ -24,11 +28,15 @@ class BottomSheetHelper {
child: Wrap( child: Wrap(
children: [ children: [
Container( Container(
decoration: const BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: backgroundContainerColor,
borderRadius: BorderRadius.vertical(top: Radius.circular(16)), borderRadius: const BorderRadius.vertical(top: Radius.circular(16)),
),
padding: EdgeInsets.only(
left: horizontalContainerPadding,
right: horizontalContainerPadding,
bottom: bottomContainerPadding,
), ),
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 16),
child: child, child: child,
), ),
SizedBox(height: 32,), SizedBox(height: 32,),
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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