categories updated.

wala_plus
Sultan khan 1 month ago
parent 9d7372ea4a
commit 782ad51823

@ -191,9 +191,14 @@ class LoginApiClient {
};
return await ApiClient().postJsonForObject((json) {
GenericResponseModel responseData = GenericResponseModel.fromJson(json);
if (responseData.logInTokenID != null) {
if (responseData.logInTokenID != null && responseData.logInTokenID!.isNotEmpty) {
AppState().postParamsObject?.setLogInTokenID = responseData.logInTokenID;
}
AppState().setUserName = nationalId;
AppState().postParamsObject?.setUserName = nationalId;
AppState().postParamsObject?.setPUserName = nationalId;
AppState().postParamsObject?.channel = 31;
AppState().postParamsObject?.versionID = 20;
return responseData;
}, url, postParams);
}
@ -244,6 +249,25 @@ class LoginApiClient {
};
return await ApiClient().postJsonForObject((json) {
GenericResponseModel responseData = GenericResponseModel.fromJson(json);
String effectiveToken = (responseData.tokenID != null && responseData.tokenID!.isNotEmpty)
? responseData.tokenID!
: ((responseData.logInTokenID != null && responseData.logInTokenID!.isNotEmpty)
? responseData.logInTokenID!
: logInTokenID);
AppState().postParamsObject?.setTokenID = effectiveToken;
AppState().postParamsObject?.setLogInTokenID = (responseData.logInTokenID != null && responseData.logInTokenID!.isNotEmpty)
? responseData.logInTokenID!
: logInTokenID;
AppState().postParamsObject?.setUserName = nationalId;
AppState().postParamsObject?.setPUserName = nationalId;
AppState().postParamsObject?.setMobileNumer = mobileNumber;
AppState().postParamsObject?.channel = 31;
AppState().postParamsObject?.versionID = 20;
AppState().setUserName = nationalId;
AppState().isAuthenticated = true;
AppState().setLogged = true;
return responseData;
}, url, postParams);
}

@ -1,10 +1,12 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:mohem_flutter_app/api/api_client.dart';
import 'package:mohem_flutter_app/app_state/app_state.dart';
import 'package:mohem_flutter_app/classes/consts.dart';
import 'package:mohem_flutter_app/models/offers_and_discounts/get_categories_list.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';
class OffersAndDiscountsApiClient {
static final OffersAndDiscountsApiClient _instance = OffersAndDiscountsApiClient._internal();
@ -68,4 +70,37 @@ class OffersAndDiscountsApiClient {
postParams,
);
}
Future<List<WalaPlusCategoryModel>> getWalaPlusCategories({BuildContext? context}) async {
String url = ApiConsts.walaPlusCategories;
String? pUserName = AppState().getUserName;
String? tokenID = AppState().postParamsObject?.tokenID;
String? logInTokenID = AppState().postParamsObject?.logInTokenID;
Map<String, dynamic> postParams = {
"VersionID": 20,
"Channel": 31,
"LanguageID": context != null ? AppState().getLanguageID(context) : 2,
"LogInTokenID": logInTokenID,
"TokenID": tokenID,
"P_USER_NAME": pUserName,
};
return await ApiClient().postJsonForObject(
(response) {
List<WalaPlusCategoryModel> categories = [];
if (response != null && response['WalaPlus_Categories'] != null) {
response['WalaPlus_Categories'].forEach((v) {
categories.add(WalaPlusCategoryModel.fromJson(v));
});
}
return categories;
},
url,
postParams,
);
}
}

@ -110,9 +110,23 @@ class AppState {
this._postParams = _postParams;
}
bool isArabic(context) => EasyLocalization.of(context)?.locale.languageCode == "ar";
bool isArabic(context) {
try {
if (context != null) {
return EasyLocalization.of(context)?.locale.languageCode == "ar";
}
} catch (_) {}
return false;
}
int getLanguageID(context) => EasyLocalization.of(context)?.locale.languageCode == "ar" ? 1 : 2;
int getLanguageID(context) {
try {
if (context != null) {
return EasyLocalization.of(context)?.locale.languageCode == "ar" ? 1 : 2;
}
} catch (_) {}
return 2;
}
String? _username;

@ -39,6 +39,9 @@ class ApiConsts {
static String mohemmSendOTPDependent = erpRest + "Mohemm_SendOTP_Dependent";
static String mohemmCheckOTPDependent = erpRest + "Mohemm_CheckOTP_Dependent";
// WalaPlus Endpoints
static String walaPlusCategories = erpRest + "WalaPlus_Categories";
static String ssoAuthRedirection = ssoBaseUrl + "/api/auth/connect";

@ -60,7 +60,7 @@ class OtpDialog {
// static String signature;
void displayDialog(BuildContext context) async {
return showDialog(
showDialog(
context: context,
barrierColor: Colors.black.withOpacity(0.63),
builder: (context) {
@ -187,6 +187,8 @@ class OtpDialog {
);
}),
);
}).then((_) {
stopTimer = true;
});
}
@ -240,30 +242,38 @@ class OtpDialog {
}
void startTimer(setState) {
if (stopTimer) return;
remainingTime--;
if (stopTimer) return;
setState(() {
displayTime = getSecondsAsDigitalClock(remainingTime);
});
try {
setState(() {
displayTime = getSecondsAsDigitalClock(remainingTime);
});
} catch (_) {}
timer = Future.delayed(const Duration(seconds: 1), () {
if (stopTimer) return;
if (remainingTime > 0) {
startTimer(setState);
} else {
setState(() {
stopTimer = true;
});
if (stopTimer) return;
try {
setState(() {
stopTimer = true;
});
} catch (_) {}
}
});
}
void hideSMSBox(context) {
stopTimer = true;
onFailure();
}
void _onOtpCallBack(String otpCode, bool? isAutofill) {
if (otpCode.length == 4) {
// stopTimer = true;
stopTimer = true;
otpFieldClear.value = otpCode;
onSuccess(otpCode, _pinPutController);
}

@ -107,6 +107,7 @@ import 'package:mohem_flutter_app/models/vacation_rule/respond_attributes_list_m
import 'package:mohem_flutter_app/models/vacation_rule/vr_item_types_list_model.dart';
import 'package:mohem_flutter_app/models/vacation_rule/wf_look_up_list_model.dart';
import 'package:mohem_flutter_app/models/validate_eit_transaction_list_model.dart';
import 'package:mohem_flutter_app/models/offers_and_discounts/wala_plus_category_model.dart';
import 'package:mohem_flutter_app/models/worklist/GetRFCEmployeeList.dart';
import 'package:mohem_flutter_app/models/worklist/get_fad_notifications.dart';
import 'package:mohem_flutter_app/models/worklist/get_favorite_replacements_model.dart';
@ -402,6 +403,7 @@ class GenericResponseModel {
GetWalaplusDepInfoModel? getWalaplusDepInfoModel;
dynamic getWalaplusDepInfoModelInternal;
List<WalaPlusCategoryModel>? walaPlusCategories;
GenericResponseModel({
this.date,
@ -677,6 +679,7 @@ class GenericResponseModel {
this.Mohemm_ENSAT_GetItgToken_Response,
this.getWalaplusDepInfoModel,
this.getWalaplusDepInfoModelInternal,
this.walaPlusCategories,
});
GenericResponseModel.fromJson(Map<String, dynamic> json) {
@ -1474,6 +1477,13 @@ class GenericResponseModel {
getWalaplusDepInfoModel = json['GET_WALAPLUS_DEP_INFO_Model'] != null ? GetWalaplusDepInfoModel.fromJson(json['GET_WALAPLUS_DEP_INFO_Model']) : null;
getWalaplusDepInfoModelInternal = json['GET_WALAPLUS_DEP_INFO_Model_Internal'];
if (json['WalaPlus_Categories'] != null) {
walaPlusCategories = <WalaPlusCategoryModel>[];
json['WalaPlus_Categories'].forEach((v) {
walaPlusCategories!.add(WalaPlusCategoryModel.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
@ -1966,6 +1976,10 @@ class GenericResponseModel {
data['GET_WALAPLUS_DEP_INFO_Model'] = this.getWalaplusDepInfoModel!.toJson();
}
if (walaPlusCategories != null) {
data['WalaPlus_Categories'] = walaPlusCategories!.map((WalaPlusCategoryModel v) => v.toJson()).toList();
}
return data;
}
}

@ -0,0 +1,36 @@
class WalaPlusCategoryModel {
String? id;
String? image;
String? imageThumb;
String? name;
String? parentId;
WalaPlusCategoryModel({
this.id,
this.image,
this.imageThumb,
this.name,
this.parentId,
});
WalaPlusCategoryModel.fromJson(Map<String, dynamic> json) {
id = json['id']?.toString();
image = json['image']?.toString();
imageThumb = json['image_thumb']?.toString();
name = json['name']?.toString();
parentId = json['parent_id']?.toString();
}
Map<String, dynamic> toJson() {
Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['image'] = image;
data['image_thumb'] = imageThumb;
data['name'] = name;
data['parent_id'] = parentId;
return data;
}
bool get isMainCategory => parentId == null || parentId!.isEmpty;
bool get isSvg => (image ?? '').toLowerCase().contains('.svg');
}

@ -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,
],
);
}
}

@ -522,6 +522,25 @@ class _LoginScreenState extends State<LoginScreen> {
}
Navigator.pop(context);
String effectiveToken = (checkOtpResponse?.tokenID != null && checkOtpResponse!.tokenID!.isNotEmpty)
? checkOtpResponse.tokenID!
: ((checkOtpResponse?.logInTokenID != null && checkOtpResponse!.logInTokenID!.isNotEmpty)
? checkOtpResponse.logInTokenID!
: logInTokenID);
AppState().postParamsObject?.setTokenID = effectiveToken;
AppState().postParamsObject?.setLogInTokenID = (checkOtpResponse?.logInTokenID != null && checkOtpResponse!.logInTokenID!.isNotEmpty)
? checkOtpResponse.logInTokenID!
: logInTokenID;
AppState().postParamsObject?.setUserName = nationalId.text.trim();
AppState().postParamsObject?.setPUserName = nationalId.text.trim();
AppState().postParamsObject?.setMobileNumer = mobileNumber;
AppState().postParamsObject?.channel = 31;
AppState().postParamsObject?.versionID = 20;
AppState().setUserName = nationalId.text.trim();
AppState().isAuthenticated = true;
AppState().setLogged = true;
Utils.showToast(AppState().isArabic(context) ? "تم التحقق بنجاح" : "Verified successfully");
if (ApiConsts.ISDependetLogin) {
Navigator.pushNamedAndRemoveUntil(context, AppRoutes.dashboardScreenForDependet, (route) => false);

@ -49,11 +49,11 @@ class _RequestDetailsState extends State<ParkingQrScreen> {
children: [
// QR Code Image
if (qrData!.getEmployeeParking!.qrCodeImageBase64 != null && qrData!.getEmployeeParking!.qrCodeImageBase64!.isNotEmpty)
Image.memory(Utils.dataFromBase64String(qrData!.getEmployeeParking!.qrCodeImageBase64!), width: 300, height: 300, fit: BoxFit.contain)
Image.memory(Utils.dataFromBase64String(qrData!.getEmployeeParking!.qrCodeImageBase64!), width: 150, height: 150, fit: BoxFit.contain)
else
Container(
width: 300,
height: 300,
width: 200,
height: 200,
color: MyColors.lightGreyEFColor,
child: Center(child: Text(LocaleKeys.noDataAvailable.tr(), style: const TextStyle(color: MyColors.grey67Color))),
),

Loading…
Cancel
Save