|
|
|
|
@ -1,6 +1,7 @@
|
|
|
|
|
import 'package:easy_localization/easy_localization.dart';
|
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
|
import 'package:flutter_svg/flutter_svg.dart';
|
|
|
|
|
import 'package:mohem_flutter_app/api/offers_and_discounts_api_client.dart';
|
|
|
|
|
import 'package:mohem_flutter_app/app_state/app_state.dart';
|
|
|
|
|
import 'package:mohem_flutter_app/classes/colors.dart';
|
|
|
|
|
import 'package:mohem_flutter_app/classes/consts.dart';
|
|
|
|
|
@ -11,6 +12,7 @@ import 'package:mohem_flutter_app/extensions/string_extensions.dart';
|
|
|
|
|
import 'package:mohem_flutter_app/extensions/widget_extensions.dart';
|
|
|
|
|
import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
|
|
|
|
|
import 'package:mohem_flutter_app/models/offers_and_discounts/get_offers_list.dart';
|
|
|
|
|
import 'package:mohem_flutter_app/models/offers_and_discounts/wala_plus_category_model.dart';
|
|
|
|
|
import 'package:mohem_flutter_app/widgets/button/default_button.dart';
|
|
|
|
|
import 'package:mohem_flutter_app/widgets/dynamic_forms/dynamic_textfield_widget.dart';
|
|
|
|
|
|
|
|
|
|
@ -24,13 +26,51 @@ class DashboardScreenForDependet extends StatefulWidget {
|
|
|
|
|
class _DashboardScreenForDependetState extends State<DashboardScreenForDependet> {
|
|
|
|
|
List<OffersListModel> offersList = [];
|
|
|
|
|
List<OffersListModel> _filteredOffersList = [];
|
|
|
|
|
List<WalaPlusCategoryModel> categoriesList = [];
|
|
|
|
|
String currentCategoryId = "0";
|
|
|
|
|
bool isLoading = false;
|
|
|
|
|
bool isCategoriesLoading = false;
|
|
|
|
|
String searchQuery = "";
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
void initState() {
|
|
|
|
|
super.initState();
|
|
|
|
|
fetchOffers();
|
|
|
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
|
|
|
if (mounted) {
|
|
|
|
|
fetchCategories();
|
|
|
|
|
fetchOffers();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Future<void> fetchCategories() async {
|
|
|
|
|
setState(() {
|
|
|
|
|
isCategoriesLoading = true;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
List<WalaPlusCategoryModel> list = await OffersAndDiscountsApiClient().getWalaPlusCategories(context: context);
|
|
|
|
|
if (list.isNotEmpty) {
|
|
|
|
|
bool isAr = mounted ? AppState().isArabic(context) : false;
|
|
|
|
|
categoriesList = [
|
|
|
|
|
WalaPlusCategoryModel(
|
|
|
|
|
id: "0",
|
|
|
|
|
name: isAr ? "الجميع" : "All",
|
|
|
|
|
image: null,
|
|
|
|
|
parentId: null,
|
|
|
|
|
),
|
|
|
|
|
...list,
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
} catch (e) {
|
|
|
|
|
print("Error fetching WalaPlus categories: $e");
|
|
|
|
|
} finally {
|
|
|
|
|
if (mounted) {
|
|
|
|
|
setState(() {
|
|
|
|
|
isCategoriesLoading = false;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Offers API placeholder - will be connected when the endpoint is provided
|
|
|
|
|
@ -44,7 +84,7 @@ class _DashboardScreenForDependetState extends State<DashboardScreenForDependet>
|
|
|
|
|
// Example:
|
|
|
|
|
// offersList = await OffersApiClient().getDependentOffers();
|
|
|
|
|
await Future.delayed(const Duration(milliseconds: 300));
|
|
|
|
|
_filteredOffersList = offersList;
|
|
|
|
|
_runFilter(searchQuery);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
print("Error fetching dependent offers: $e");
|
|
|
|
|
} finally {
|
|
|
|
|
@ -56,18 +96,28 @@ class _DashboardScreenForDependetState extends State<DashboardScreenForDependet>
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Future<void> _onRefresh() async {
|
|
|
|
|
await Future.wait([
|
|
|
|
|
fetchCategories(),
|
|
|
|
|
fetchOffers(),
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void _runFilter(String query) {
|
|
|
|
|
searchQuery = query;
|
|
|
|
|
if (query.isEmpty) {
|
|
|
|
|
_filteredOffersList = offersList;
|
|
|
|
|
} else {
|
|
|
|
|
List<OffersListModel> temp = offersList;
|
|
|
|
|
if (currentCategoryId != "0") {
|
|
|
|
|
temp = temp.where((offer) => offer.categoryId?.toString() == currentCategoryId).toList();
|
|
|
|
|
}
|
|
|
|
|
if (query.isNotEmpty) {
|
|
|
|
|
bool isAr = AppState().isArabic(context);
|
|
|
|
|
_filteredOffersList =
|
|
|
|
|
offersList.where((offer) {
|
|
|
|
|
temp =
|
|
|
|
|
temp.where((offer) {
|
|
|
|
|
String title = isAr ? (offer.titleAr ?? "") : (offer.titleEn ?? "");
|
|
|
|
|
return title.toLowerCase().contains(query.toLowerCase());
|
|
|
|
|
}).toList();
|
|
|
|
|
}
|
|
|
|
|
_filteredOffersList = temp;
|
|
|
|
|
setState(() {});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -78,6 +128,7 @@ class _DashboardScreenForDependetState extends State<DashboardScreenForDependet>
|
|
|
|
|
onTap: () {
|
|
|
|
|
ApiConsts.selectedMemberType = MemberType.employee;
|
|
|
|
|
ApiConsts.ISDependetLogin = false;
|
|
|
|
|
AppState().setPostParamsInitConfig();
|
|
|
|
|
Navigator.pushNamedAndRemoveUntil(context, AppRoutes.login, (Route<dynamic> route) => false);
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
@ -115,7 +166,7 @@ class _DashboardScreenForDependetState extends State<DashboardScreenForDependet>
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
body: RefreshIndicator(
|
|
|
|
|
onRefresh: fetchOffers,
|
|
|
|
|
onRefresh: _onRefresh,
|
|
|
|
|
color: MyColors.textMixColor,
|
|
|
|
|
child: SingleChildScrollView(
|
|
|
|
|
physics: const AlwaysScrollableScrollPhysics(parent: BouncingScrollPhysics()),
|
|
|
|
|
@ -139,6 +190,63 @@ class _DashboardScreenForDependetState extends State<DashboardScreenForDependet>
|
|
|
|
|
),
|
|
|
|
|
20.height,
|
|
|
|
|
|
|
|
|
|
// Categories Section
|
|
|
|
|
if (isCategoriesLoading)
|
|
|
|
|
_buildCategoriesShimmer()
|
|
|
|
|
else if (categoriesList.isNotEmpty) ...[
|
|
|
|
|
LocaleKeys.browseCategories.tr().toText17(isBold: true, color: MyColors.darkTextColor),
|
|
|
|
|
12.height,
|
|
|
|
|
SizedBox(
|
|
|
|
|
height: 110.0,
|
|
|
|
|
child: ListView.separated(
|
|
|
|
|
shrinkWrap: true,
|
|
|
|
|
physics: const BouncingScrollPhysics(),
|
|
|
|
|
scrollDirection: Axis.horizontal,
|
|
|
|
|
itemCount: categoriesList.length,
|
|
|
|
|
separatorBuilder: (cxt, index) => 12.width,
|
|
|
|
|
itemBuilder: (BuildContext cxt, int index) {
|
|
|
|
|
WalaPlusCategoryModel item = categoriesList[index];
|
|
|
|
|
bool isSelected = currentCategoryId == item.id;
|
|
|
|
|
String title = item.id == "0" ? (isAr ? "الجميع" : "All") : (item.name ?? "");
|
|
|
|
|
|
|
|
|
|
return AspectRatio(
|
|
|
|
|
aspectRatio: 1 / 1,
|
|
|
|
|
child: InkWell(
|
|
|
|
|
borderRadius: BorderRadius.circular(15),
|
|
|
|
|
onTap: () {
|
|
|
|
|
setState(() {
|
|
|
|
|
currentCategoryId = item.id ?? "0";
|
|
|
|
|
});
|
|
|
|
|
_runFilter(searchQuery);
|
|
|
|
|
},
|
|
|
|
|
child: Column(
|
|
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
|
children: [
|
|
|
|
|
Row(
|
|
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
|
children: [
|
|
|
|
|
_buildCategoryIcon(item),
|
|
|
|
|
if (isSelected)
|
|
|
|
|
const Icon(Icons.check_circle_rounded, color: MyColors.greenColor, size: 16.0),
|
|
|
|
|
],
|
|
|
|
|
).expanded,
|
|
|
|
|
title.toText10(
|
|
|
|
|
isBold: isSelected,
|
|
|
|
|
color: isSelected ? MyColors.textMixColor : MyColors.darkTextColor,
|
|
|
|
|
maxlines: 2,
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
).paddingOnly(left: 10, right: 10, bottom: 10, top: 12).expanded.objectContainerView(disablePadding: true),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
20.height,
|
|
|
|
|
],
|
|
|
|
|
|
|
|
|
|
// Header
|
|
|
|
|
Row(
|
|
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
|
|
|
@ -229,4 +337,53 @@ class _DashboardScreenForDependetState extends State<DashboardScreenForDependet>
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Widget _buildCategoryIcon(WalaPlusCategoryModel item) {
|
|
|
|
|
if (item.id == "0" || item.image == null || item.image!.isEmpty) {
|
|
|
|
|
return const Icon(Icons.grid_view_rounded, size: 24, color: MyColors.textMixColor);
|
|
|
|
|
}
|
|
|
|
|
if (item.isSvg) {
|
|
|
|
|
return SvgPicture.network(
|
|
|
|
|
item.image!,
|
|
|
|
|
width: 25,
|
|
|
|
|
height: 25,
|
|
|
|
|
fit: BoxFit.contain,
|
|
|
|
|
placeholderBuilder: (_) => const SizedBox(width: 25, height: 25),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
return Image.network(
|
|
|
|
|
item.imageThumb ?? item.image!,
|
|
|
|
|
width: 25,
|
|
|
|
|
height: 25,
|
|
|
|
|
fit: BoxFit.contain,
|
|
|
|
|
errorBuilder: (_, __, ___) => const Icon(Icons.category_outlined, size: 24, color: MyColors.grey70Color),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Widget _buildCategoriesShimmer() {
|
|
|
|
|
return Column(
|
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
|
children: [
|
|
|
|
|
Container(width: 120, height: 16, color: Colors.white).toShimmer(),
|
|
|
|
|
12.height,
|
|
|
|
|
SizedBox(
|
|
|
|
|
height: 110.0,
|
|
|
|
|
child: ListView.separated(
|
|
|
|
|
shrinkWrap: true,
|
|
|
|
|
scrollDirection: Axis.horizontal,
|
|
|
|
|
physics: const NeverScrollableScrollPhysics(),
|
|
|
|
|
itemCount: 4,
|
|
|
|
|
separatorBuilder: (_, __) => 12.width,
|
|
|
|
|
itemBuilder: (_, __) => AspectRatio(
|
|
|
|
|
aspectRatio: 1 / 1,
|
|
|
|
|
child: Container(
|
|
|
|
|
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(15)),
|
|
|
|
|
).toShimmer(),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
20.height,
|
|
|
|
|
],
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|