You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
mohemm-flutter-app/lib/ui/landing/wala_plus_retailer_details_...

631 lines
22 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter/services.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/consts.dart';
import 'package:mohem_flutter_app/config/routes.dart';
import 'package:mohem_flutter_app/extensions/int_extensions.dart';
import 'package:mohem_flutter_app/models/offers_and_discounts/wala_plus_retailer_model.dart';
import 'package:mohem_flutter_app/widgets/app_bar_widget.dart';
import 'package:shimmer/shimmer.dart';
import 'package:url_launcher/url_launcher.dart';
class WalaPlusRetailerDetailsScreen extends StatefulWidget {
final WalaPlusRetailerModel? retailer;
final String? retailerId;
const WalaPlusRetailerDetailsScreen({
Key? key,
this.retailer,
this.retailerId,
}) : super(key: key);
@override
State<WalaPlusRetailerDetailsScreen> createState() => _WalaPlusRetailerDetailsScreenState();
}
class _WalaPlusRetailerDetailsScreenState extends State<WalaPlusRetailerDetailsScreen> {
WalaPlusRetailerModel? _retailer;
bool _isLoading = false;
String? _effectiveRetailerId;
@override
void initState() {
super.initState();
_retailer = widget.retailer;
_effectiveRetailerId = widget.retailerId ?? widget.retailer?.id;
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
if (_retailer == null && _effectiveRetailerId == null) {
Object? args = ModalRoute.of(context)?.settings.arguments;
if (args is WalaPlusRetailerModel) {
_retailer = args;
_effectiveRetailerId = args.id;
} else if (args is String) {
_effectiveRetailerId = args;
}
}
if (_retailer == null && _effectiveRetailerId != null && !_isLoading) {
_fetchRetailerDetails();
}
}
Future<void> _fetchRetailerDetails() async {
if (_effectiveRetailerId == null) return;
setState(() {
_isLoading = true;
});
try {
WalaPlusRetailerModel? result = await OffersAndDiscountsApiClient().getWalaPlusRetailerById(
retailerId: _effectiveRetailerId!,
context: context,
);
if (result != null && mounted) {
setState(() {
_retailer = result;
});
}
} catch (e) {
debugPrint("Error fetching WalaPlus retailer by ID: $e");
} finally {
if (mounted) {
setState(() {
_isLoading = false;
});
}
}
}
void _onUseOffer() async {
String? website = _retailer?.resolvedWebsite;
if (website != null && website.isNotEmpty) {
try {
Uri uri = Uri.parse(website);
if (!uri.hasScheme) {
uri = Uri.parse("https://$website");
}
await launchUrl(uri, mode: LaunchMode.externalApplication);
return;
} catch (e) {
debugPrint("Error launching website: $e");
}
}
// If promo pin exists, display a dialog with the promo code to copy
if (_retailer?.pin != null && _retailer!.pin!.isNotEmpty) {
_showPromoCodeDialog(_retailer!.pin!);
return;
}
// Otherwise show terms/usage dialog
_showTermsDialog();
}
void _showPromoCodeDialog(String pin) {
bool isAr = AppState().isArabic(context);
showDialog(
context: context,
builder: (cxt) => AlertDialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
title: Text(
isAr ? "كود الخصم" : "Promo Code",
style: const TextStyle(fontWeight: FontWeight.bold),
),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
decoration: BoxDecoration(
color: const Color(0xFFF3F4F6),
borderRadius: BorderRadius.circular(10),
border: Border.all(color: const Color(0xFF20A4A8), width: 1.2),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
pin,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
letterSpacing: 2,
color: Color(0xFF1E293B),
),
),
IconButton(
icon: const Icon(Icons.copy_rounded, color: Color(0xFF20A4A8)),
onPressed: () {
Clipboard.setData(ClipboardData(text: pin));
Navigator.pop(cxt);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(isAr ? "تم نسخ الكود بنجاح" : "Promo code copied"),
duration: const Duration(seconds: 2),
),
);
},
),
],
),
),
],
),
actions: [
TextButton(
onPressed: () => Navigator.pop(cxt),
child: Text(isAr ? "إغلاق" : "Close", style: const TextStyle(color: Color(0xFF20A4A8))),
),
],
),
);
}
void _showTermsDialog() {
bool isAr = AppState().isArabic(context);
String content = _retailer?.howToUse ??
_retailer?.arHowToUse ??
_retailer?.enHowToUse ??
_retailer?.usage?.toString() ??
(isAr ? "تطبق الشروط والأحكام الخاصة بالعرض." : "Terms and conditions apply for this offer.");
// Clean html tags if simple
content = content.replaceAll(RegExp(r'<[^>]*>|&[^;]+;'), ' ').trim();
showModalBottomSheet(
context: context,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
),
builder: (cxt) => SafeArea(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
isAr ? "الشروط والأحكام" : "Terms and Conditions",
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Color(0xFF1E293B),
),
),
IconButton(
icon: const Icon(Icons.close),
onPressed: () => Navigator.pop(cxt),
),
],
),
const SizedBox(height: 12),
Text(
content,
style: const TextStyle(fontSize: 14, color: Color(0xFF4B5563), height: 1.5),
),
const SizedBox(height: 20),
],
),
),
),
);
}
@override
Widget build(BuildContext context) {
bool isAr = AppState().isArabic(context);
String title = _retailer?.name ?? (isAr ? "تفاصيل العرض" : "Offer Details");
return Scaffold(
backgroundColor: const Color(0xFFF8F9FA),
appBar: AppBarWidget(
context,
title: title,
showHomeButton: true,
onHomeTapped: () {
if (ApiConsts.isDependetLogin) {
Navigator.popUntil(context, ModalRoute.withName(AppRoutes.dashboardScreenForDependet));
} else {
Navigator.popUntil(context, ModalRoute.withName(AppRoutes.dashboard));
}
},
),
body: _isLoading && _retailer == null
? _buildLoadingSkeleton()
: Column(
children: [
Expanded(
child: SingleChildScrollView(
physics: const BouncingScrollPhysics(),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(22),
border: Border.all(color: const Color(0xFFF1F5F9), width: 1.2),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.04),
blurRadius: 12,
offset: const Offset(0, 4),
),
],
),
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 1. Top Banner Image with rounded corners
ClipRRect(
borderRadius: BorderRadius.circular(16),
child: SizedBox(
height: 190,
width: double.infinity,
child: (_retailer?.image != null && _retailer!.image!.isNotEmpty)
? Image.network(
_retailer!.image!,
fit: BoxFit.cover,
errorBuilder: (_, __, ___) => Container(
color: const Color(0xFFF3F4F6),
child: const Icon(Icons.broken_image, size: 48, color: Color(0xFF9CA3AF)),
),
)
: Container(
color: const Color(0xFFF3F4F6),
child: const Icon(Icons.image_outlined, size: 48, color: Color(0xFF9CA3AF)),
),
),
),
14.height,
// 2. Brand Strip (Human Resources - Retailer Logo - Dr Sulaiman Al Habib)
_buildBrandStrip(isAr),
18.height,
// 3. Retailer Name (Centered, Bold)
Center(
child: Text(
_retailer?.name ?? "",
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
color: Color(0xFF1E293B),
),
),
),
18.height,
// 4. 3 Info Badges Row (Store Type, Validity, City)
_buildInfoBadgesRow(isAr),
22.height,
// 5. Description Section
Text(
isAr ? "الوصف:" : "description:",
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Color(0xFF1E293B),
),
),
8.height,
Text(
_getCleanDescription(),
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w400,
color: Color(0xFF4B5563),
height: 1.45,
),
),
16.height,
// 6. Terms and Conditions Link (Right aligned)
Align(
alignment: isAr ? Alignment.centerLeft : Alignment.centerRight,
child: InkWell(
onTap: _showTermsDialog,
child: Text(
isAr ? "الشروط والأحكام" : "terms and conditions",
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Color(0xFF20A4A8),
decoration: TextDecoration.underline,
),
),
),
),
],
),
),
),
),
// 7. Bottom Action Button
Container(
padding: const EdgeInsets.fromLTRB(16, 10, 16, 20),
color: Colors.white,
child: SizedBox(
width: double.infinity,
height: 50,
child: ElevatedButton(
onPressed: _onUseOffer,
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF20A4A8),
foregroundColor: Colors.white,
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
child: Text(
isAr ? "استخدام العرض" : "Use Offer",
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w700,
color: Colors.white,
),
),
),
),
),
],
),
);
}
Widget _buildBrandStrip(bool isAr) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// Left: Human Resources branding
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: const [
Text(
"الموارد البشرية",
style: TextStyle(
color: Color(0xFFED1C2B),
fontSize: 10,
fontWeight: FontWeight.w700,
),
),
Text(
"Human Resources",
style: TextStyle(
color: Color(0xFFED1C2B),
fontSize: 8.5,
fontWeight: FontWeight.w600,
),
),
],
),
// Center: Retailer Logo
if (_retailer?.logo != null && _retailer!.logo!.isNotEmpty)
Image.network(
_retailer!.logo!,
height: 36,
width: 36,
fit: BoxFit.contain,
errorBuilder: (_, __, ___) => const SizedBox(width: 36, height: 36),
)
else
const SizedBox(width: 36, height: 36),
// Right: Dr Sulaiman Al Habib branding with red cross
Row(
mainAxisSize: MainAxisSize.min,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
children: const [
Text(
"سليمان الحبيب",
style: TextStyle(
color: Color(0xFF4B5563),
fontSize: 9,
fontWeight: FontWeight.w700,
),
),
Text(
"DR SULAIMAN AL HABIB",
style: TextStyle(
color: Color(0xFF4B5563),
fontSize: 8,
fontWeight: FontWeight.w600,
),
),
],
),
const SizedBox(width: 5),
SvgPicture.asset(
"assets/icons/mazaya_brand.svg",
height: 22,
fit: BoxFit.contain,
),
],
),
],
);
}
Widget _buildInfoBadgesRow(bool isAr) {
String storeTypeText = (_retailer?.type?.toLowerCase() == 'online')
? (isAr ? "عبر الإنترنت" : "online")
: (isAr ? "في المتجر" : "in store");
IconData storeIcon = (_retailer?.type?.toLowerCase() == 'online')
? Icons.language_rounded
: Icons.storefront_rounded;
String cityText = _retailer?.displayCity ?? "";
if (cityText.isEmpty) {
cityText = isAr ? "الرياض" : "Riyadh";
}
String validityText = isAr ? "٣ أشهر" : "3 Month";
return Row(
children: [
Expanded(
child: _buildInfoCard(
icon: storeIcon,
label: storeTypeText,
),
),
12.width,
Expanded(
child: _buildInfoCard(
icon: Icons.check_circle_outline_rounded,
label: validityText,
),
),
12.width,
Expanded(
child: _buildInfoCard(
icon: Icons.location_on_rounded,
label: cityText,
),
),
],
);
}
Widget _buildInfoCard({required IconData icon, required String label}) {
return Container(
padding: const EdgeInsets.symmetric(vertical: 14, horizontal: 8),
decoration: BoxDecoration(
color: const Color(0xFFF4F6F9),
borderRadius: BorderRadius.circular(14),
),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(icon, color: const Color(0xFF20A4A8), size: 26),
const SizedBox(height: 8),
Text(
label,
textAlign: TextAlign.center,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF334155),
),
),
],
),
);
}
String _getCleanDescription() {
String desc = _retailer?.description ?? _retailer?.info ?? _retailer?.offerTitle ?? "";
if (desc.isEmpty) {
return AppState().isArabic(context) ? "لا توجد تفاصيل إضافية متاحة." : "No additional details available.";
}
// Remove HTML tags if string contains them
return desc.replaceAll(RegExp(r'<[^>]*>|&[^;]+;'), ' ').trim();
}
Widget _buildLoadingSkeleton() {
return Shimmer.fromColors(
baseColor: const Color(0xFFE2E8F0),
highlightColor: const Color(0xFFF8FAFC),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(22),
),
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
height: 190,
width: double.infinity,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
),
),
const SizedBox(height: 16),
Center(
child: Container(
height: 24,
width: 160,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(6),
),
),
),
const SizedBox(height: 20),
Row(
children: List.generate(
3,
(index) => Expanded(
child: Container(
height: 75,
margin: EdgeInsets.symmetric(horizontal: index == 1 ? 8 : 0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(14),
),
),
),
),
),
const SizedBox(height: 24),
Container(
height: 16,
width: 100,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(4),
),
),
const SizedBox(height: 10),
Container(
height: 14,
width: double.infinity,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(4),
),
),
const SizedBox(height: 6),
Container(
height: 14,
width: 220,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(4),
),
),
],
),
),
),
);
}
}