Compare commits
2 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
9d7372ea4a | 3 weeks ago |
|
|
c0497201b6 | 3 weeks ago |
@ -0,0 +1,232 @@
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.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';
|
||||
import 'package:mohem_flutter_app/classes/utils.dart';
|
||||
import 'package:mohem_flutter_app/config/routes.dart';
|
||||
import 'package:mohem_flutter_app/extensions/int_extensions.dart';
|
||||
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/widgets/button/default_button.dart';
|
||||
import 'package:mohem_flutter_app/widgets/dynamic_forms/dynamic_textfield_widget.dart';
|
||||
|
||||
class DashboardScreenForDependet extends StatefulWidget {
|
||||
const DashboardScreenForDependet({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<DashboardScreenForDependet> createState() => _DashboardScreenForDependetState();
|
||||
}
|
||||
|
||||
class _DashboardScreenForDependetState extends State<DashboardScreenForDependet> {
|
||||
List<OffersListModel> offersList = [];
|
||||
List<OffersListModel> _filteredOffersList = [];
|
||||
bool isLoading = false;
|
||||
String searchQuery = "";
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
fetchOffers();
|
||||
}
|
||||
|
||||
/// Offers API placeholder - will be connected when the endpoint is provided
|
||||
Future<void> fetchOffers() async {
|
||||
setState(() {
|
||||
isLoading = true;
|
||||
});
|
||||
|
||||
try {
|
||||
// TODO: Call Dependent Offers API here once provided
|
||||
// Example:
|
||||
// offersList = await OffersApiClient().getDependentOffers();
|
||||
await Future.delayed(const Duration(milliseconds: 300));
|
||||
_filteredOffersList = offersList;
|
||||
} catch (e) {
|
||||
print("Error fetching dependent offers: $e");
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _runFilter(String query) {
|
||||
searchQuery = query;
|
||||
if (query.isEmpty) {
|
||||
_filteredOffersList = offersList;
|
||||
} else {
|
||||
bool isAr = AppState().isArabic(context);
|
||||
_filteredOffersList =
|
||||
offersList.where((offer) {
|
||||
String title = isAr ? (offer.titleAr ?? "") : (offer.titleEn ?? "");
|
||||
return title.toLowerCase().contains(query.toLowerCase());
|
||||
}).toList();
|
||||
}
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
void _performLogout() {
|
||||
Utils.confirmDialog(
|
||||
context,
|
||||
AppState().isArabic(context) ? "هل أنت متأكد من تسجيل الخروج؟" : "Are you sure you want to log out?",
|
||||
onTap: () {
|
||||
ApiConsts.selectedMemberType = MemberType.employee;
|
||||
ApiConsts.ISDependetLogin = false;
|
||||
Navigator.pushNamedAndRemoveUntil(context, AppRoutes.login, (Route<dynamic> route) => false);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
bool isAr = AppState().isArabic(context);
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: MyColors.backgroundColor,
|
||||
appBar: AppBar(
|
||||
backgroundColor: Colors.white,
|
||||
elevation: 0,
|
||||
surfaceTintColor: Colors.transparent,
|
||||
automaticallyImplyLeading: false,
|
||||
title: Row(children: [(isAr ? "عروض التابعين" : "Offers & Discounts").toText20(color: MyColors.darkTextColor, isBold: true)]),
|
||||
actions: [
|
||||
// Logout button
|
||||
Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
decoration: BoxDecoration(color: MyColors.redColor.withOpacity(0.08), borderRadius: BorderRadius.circular(8)),
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
onTap: _performLogout,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [const Icon(Icons.logout_rounded, size: 18, color: MyColors.redColor), 6.width, (isAr ? "خروج" : "Logout").toText13(color: MyColors.redColor)],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: RefreshIndicator(
|
||||
onRefresh: fetchOffers,
|
||||
color: MyColors.textMixColor,
|
||||
child: SingleChildScrollView(
|
||||
physics: const AlwaysScrollableScrollPhysics(parent: BouncingScrollPhysics()),
|
||||
padding: const EdgeInsets.all(21),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Search input
|
||||
DynamicTextFieldWidget(
|
||||
LocaleKeys.search.tr(),
|
||||
LocaleKeys.searchItems.tr(),
|
||||
isEnable: true,
|
||||
suffixIconData: Icons.search,
|
||||
isPopup: false,
|
||||
lines: 1,
|
||||
isInputTypeNum: false,
|
||||
isReadOnly: false,
|
||||
onChange: (String value) {
|
||||
_runFilter(value);
|
||||
},
|
||||
),
|
||||
20.height,
|
||||
|
||||
// Header
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
(isAr ? "العروض المتاحة" : "Available Offers").toText18(isBold: true, color: MyColors.darkTextColor),
|
||||
if (offersList.isNotEmpty) "${_filteredOffersList.length} ${isAr ? "عرض" : "offers"}".toText12(color: MyColors.grey70Color),
|
||||
],
|
||||
),
|
||||
16.height,
|
||||
|
||||
// Content Area
|
||||
if (isLoading)
|
||||
const Center(child: Padding(padding: EdgeInsets.symmetric(vertical: 60), child: CircularProgressIndicator(color: MyColors.textMixColor)))
|
||||
else if (_filteredOffersList.isEmpty)
|
||||
_buildEmptyOffersWidget(isAr)
|
||||
else
|
||||
GridView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2, childAspectRatio: 162 / 266, crossAxisSpacing: 12, mainAxisSpacing: 12),
|
||||
itemCount: _filteredOffersList.length,
|
||||
itemBuilder: (context, index) {
|
||||
return _buildOfferCard(_filteredOffersList[index], isAr);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildEmptyOffersWidget(bool isAr) {
|
||||
return Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 60, horizontal: 24),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Container(
|
||||
width: 80,
|
||||
height: 80,
|
||||
decoration: BoxDecoration(color: MyColors.textMixColor.withOpacity(0.1), shape: BoxShape.circle),
|
||||
child: const Icon(Icons.local_offer_outlined, size: 40, color: MyColors.textMixColor),
|
||||
),
|
||||
16.height,
|
||||
(isAr ? "لا توجد عروض متاحة حالياً" : "No Offers Available Currently").toText16(isBold: true, color: MyColors.darkTextColor),
|
||||
8.height,
|
||||
(isAr ? "سيتم إتاحة العروض الخاصة بك قريباً" : "Exclusive offers for dependents will be available soon.").toText13(color: MyColors.grey70Color),
|
||||
24.height,
|
||||
DefaultButton(isAr ? "تحديث" : "Refresh", fetchOffers, colors: const [MyColors.gradiantStartColor, MyColors.gradiantEndColor]),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildOfferCard(OffersListModel item, bool isAr) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
boxShadow: [BoxShadow(color: const Color(0xff000000).withOpacity(.05), blurRadius: 26, offset: const Offset(0, -3))],
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
AspectRatio(
|
||||
aspectRatio: 118 / 127,
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
child:
|
||||
(item.bannerImage != null && item.bannerImage!.isNotEmpty)
|
||||
? Image.network(
|
||||
item.bannerImage!,
|
||||
fit: BoxFit.contain,
|
||||
errorBuilder: (_, __, ___) => Container(color: MyColors.greyF7Color, child: const Icon(Icons.broken_image, color: MyColors.grey70Color)),
|
||||
)
|
||||
: Container(color: MyColors.greyF7Color, child: const Icon(Icons.image_outlined, color: MyColors.grey70Color)),
|
||||
),
|
||||
),
|
||||
8.height,
|
||||
(isAr ? (item.titleAr ?? "") : (item.titleEn ?? "")).toText16(isBold: true, color: MyColors.darkTextColor, maxlines: 1),
|
||||
if (item.discountDescription != null && item.discountDescription!.isNotEmpty) ...[4.height, item.discountDescription!.toText14(isBold: true, maxlines: 1)],
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue