import 'dart:io'; import 'package:business_panel/core/config/app_colors.dart'; import 'package:business_panel/domain/entities/discount_type_entity.dart'; import 'package:business_panel/gen/assets.gen.dart'; import 'package:business_panel/presentation/discount/bloc/discount_bloc.dart'; import 'package:business_panel/presentation/discount/bloc/discount_event.dart'; import 'package:business_panel/presentation/discount/bloc/discount_state.dart'; import 'package:business_panel/presentation/widgets/info_popup.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:image_picker/image_picker.dart'; import 'package:persian_datetime_picker/persian_datetime_picker.dart'; class AddDiscountPage extends StatelessWidget { const AddDiscountPage({super.key}); @override Widget build(BuildContext context) { return BlocProvider( create: (_) => DiscountBloc(), child: const _AddDiscountView(), ); } } class _AddDiscountView extends StatefulWidget { const _AddDiscountView(); @override State<_AddDiscountView> createState() => _AddDiscountViewState(); } class _AddDiscountViewState extends State<_AddDiscountView> { final _nameController = TextEditingController(); final _descController = TextEditingController(); final _priceController = TextEditingController(); final _discountPriceController = TextEditingController(); @override void dispose() { _nameController.dispose(); _descController.dispose(); _priceController.dispose(); _discountPriceController.dispose(); super.dispose(); } Future _pickValidityDates(BuildContext context) async { Jalali? startDate = await showPersianDatePicker( context: context, initialDate: Jalali.now(), firstDate: Jalali.now(), lastDate: Jalali(1500), ); if (startDate == null || !context.mounted) return; TimeOfDay? startTime = await showTimePicker( context: context, initialTime: TimeOfDay.now(), ); if (startTime == null || !context.mounted) return; Jalali? endDate = await showPersianDatePicker( context: context, initialDate: startDate, firstDate: startDate, lastDate: Jalali(1500), ); if (endDate == null || !context.mounted) return; TimeOfDay? endTime = await showTimePicker( context: context, initialTime: startTime, ); if (endTime == null || !context.mounted) return; final DateTime startDateTime = startDate.toDateTime().add( Duration(hours: startTime.hour, minutes: startTime.minute), ); final DateTime endDateTime = endDate.toDateTime().add( Duration(hours: endTime.hour, minutes: endTime.minute), ); context.read().add( ValidityDateChanged(startDate: startDateTime, endDate: endDateTime), ); } final List discountTypes = [ DiscountTypeEntity(id: "dda9aef3-367e-48b3-ba35-8e7744d99659", name: "ساعت خوش"), DiscountTypeEntity(id: "57577fac-7d06-4b2e-a577-7d2ce98fee58", name: "رفیق بازی"), DiscountTypeEntity(id: "06156635-048b-4ed9-b5d5-2f89824435e1", name: "محصول جانبی رایگان"), DiscountTypeEntity(id: "16e7d1e9-29c4-4320-9869-3c986cc20734", name: "کالای مکمل"), DiscountTypeEntity(id: "fb600fbb-bab4-4e63-a25b-d8ffdacb7c09", name: "پلکانی"), DiscountTypeEntity(id: "488ef29e-415d-4362-b984-509faabac058", name: "دعوت نامه طلایی"), DiscountTypeEntity(id: "e03e5823-27d8-4f45-bd6c-f7c11822ec7a", name: "بازگشت وجه"), DiscountTypeEntity(id: "bb0eea57-b630-4373-baff-72df72921e67", name: "سایر"), ]; @override Widget build(BuildContext context) { return Scaffold( appBar: _buildCustomAppBar(context), body: BlocListener( listener: (context, state) { if (state.isSuccess) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text("تخفیف با موفقیت ثبت شد!"), backgroundColor: Colors.green), ); // میتوانید به صفحه دیگری ناوبری کنید // Navigator.of(context).pop(); } if (state.errorMessage != null) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(state.errorMessage!), backgroundColor: Colors.red), ); } }, child: SingleChildScrollView( padding: const EdgeInsets.all(24), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( "تعریف تخفیف جدید", style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20), ), const SizedBox(height: 24), _buildSectionTitle( title: "بارگذاری عکس از محصول", popupTitle: "یه عکس خوب، یه فروش خوب‌تر!", isMandatory: true, infoText: "عکس واضح، باکیفیت و واقعی از محصولت بذار. ترجیحا از عکس‌های اینترنتی یا تبلیغاتی استفاده نکن.", iconPath: Assets.icons.camera, ), const SizedBox(height: 16), _buildImagePickers(), const SizedBox(height: 30), _buildTextField( controller: _nameController, label: "نام محصول", isRequired: true, hint: "وافل شکلات فندقی", onChanged: (value) => context.read().add( ProductNameChanged(value), ), ), const SizedBox(height: 30), _buildDiscountTypeDropdown(), const SizedBox(height: 30), _buildTextField( controller: _descController, label: "توضیح برای تخفیف", hint: "مثلاً عصرونه، با ۵٪ تخفیف مهمون ما باش! ", isRequired: true, maxLines: 4, maxLength: 200, onChanged: (value) => context.read().add( DescriptionChanged(value), ), ), const SizedBox(height: 30), _buildDateTimePicker(), const SizedBox(height: 30), _buildTimeRangePicker(context), const SizedBox(height: 30), _buildTextField( controller: _priceController, label: "قیمت بدون تخفیف", isRequired: true, hint: "مثلاً 240000 تومان", keyboardType: TextInputType.number, onChanged: (value) => context.read().add(PriceChanged(value)), ), const SizedBox(height: 30), _buildTextField( controller: _discountPriceController, label: "قیمت با تخفیف", hint: "مثلاً 200000 تومان", isRequired: true, keyboardType: TextInputType.number, onChanged: (value) => context.read().add( DiscountedPriceChanged(value), ), ), const SizedBox(height: 30), _buildNotificationRadiusSlider(), const SizedBox(height: 30), SizedBox( width: double.infinity, child: BlocBuilder( builder: (context, state) { return ElevatedButton( onPressed: state.isSubmitting ? null : () { context.read().add(SubmitDiscount()); }, child: state.isSubmitting ? const CircularProgressIndicator(color: Colors.white) : const Text("ثبت تخفیف"), ); }, ), ), const SizedBox(height: 30), ], ), ), )); } Widget _buildSectionTitle({ required String title, String? popupTitle, bool isMandatory = false, String? infoText, String? iconPath, }) { return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ if (infoText != null && iconPath != null) IconButton( onPressed: () => showInfoDialog( context, title: popupTitle ?? title, content: infoText, iconPath: iconPath, ), icon: SvgPicture.asset(Assets.icons.infoCircle, width: 17), ), Text( title, style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 14), ), if (isMandatory) const Text(' *', style: TextStyle(color: Colors.red, fontSize: 17)), ], ); } Widget _buildImagePickers() { return BlocBuilder( builder: (context, state) { return Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: List.generate(2, (index) { final imagePath = state.productImages.length > index ? state.productImages[index] : null; return GestureDetector( onTap: () async { final ImagePicker picker = ImagePicker(); final XFile? image = await picker.pickImage( source: ImageSource.gallery, ); if (image != null && context.mounted) { context.read().add( ProductImageAdded(image.path, index), ); } }, child: Container( width: 125, height: 125, decoration: BoxDecoration( color: AppColors.uploadElevated, borderRadius: BorderRadius.circular(12), border: Border.all(color: AppColors.uploadElevated), image: imagePath != null ? DecorationImage( image: FileImage(File(imagePath)), fit: BoxFit.cover, ) : null, ), child: imagePath == null ? Center( child: SvgPicture.asset( Assets.icons.addPic, width: 60, ), ) : null, ), ); }), ); }, ); } Widget _buildDiscountTypeDropdown() { return DropdownButtonFormField( value: context.watch().state.discountTypeId, icon: SvgPicture.asset( Assets.icons.arrowDown, width: 24, color: Colors.black, ), menuMaxHeight: 400, hint: const Text("نوع تخفیف را انتخاب کنید"), decoration: _inputDecoration("نوع تخفیف", isRequired: true).copyWith( contentPadding: const EdgeInsets.symmetric( vertical: 14, horizontal: 20, ), ), borderRadius: BorderRadius.circular(12.0), items: discountTypes.map((type) { return DropdownMenuItem(value: type.id, child: Text(type.name)); }).toList(), onChanged: (value) { if (value != null) { context.read().add(DiscountTypeChanged(value)); } }, ); } Widget _buildDateTimePicker() { return BlocBuilder( buildWhen: (previous, current) => previous.startDate != current.startDate || previous.endDate != current.endDate, builder: (context, state) { String displayText = "انتخاب تاریخ"; if (state.startDate != null && state.endDate != null) { final jalaliStart = DateTimeExtensions(state.startDate!).toJalali(); final jalaliEnd = DateTimeExtensions(state.endDate!).toJalali(); final startFormatted = '${jalaliStart.year}/${jalaliStart.month.toString().padLeft(2, '0')}/${jalaliStart.day.toString().padLeft(2, '0')} - ${state.startDate!.hour.toString().padLeft(2, '0')}:${state.startDate!.minute.toString().padLeft(2, '0')}'; final endFormatted = '${jalaliEnd.year}/${jalaliEnd.month.toString().padLeft(2, '0')}/${jalaliEnd.day.toString().padLeft(2, '0')} - ${state.endDate!.hour.toString().padLeft(2, '0')}:${state.endDate!.minute.toString().padLeft(2, '0')}'; displayText = 'از $startFormatted\nتا $endFormatted'; } return InkWell( onTap: () => _pickValidityDates(context), child: InputDecorator( decoration: _inputDecoration( "تاریخ اعتبار تخفیف", isRequired: true, ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( child: Text( displayText, textDirection: TextDirection.rtl, style: const TextStyle(fontSize: 15), // Optional: for better fit ), ), SvgPicture.asset(Assets.icons.calendarSearch), ], ), ), ); }, ); } Widget _buildTimeRangePicker(BuildContext context) { return BlocBuilder( buildWhen: (previous, current) => previous.startTime != current.startTime || previous.endTime != current.endTime, builder: (context, state) { String displayText = "انتخاب بازه زمانی"; if (state.startTime != null && state.endTime != null) { displayText = 'از ساعت ${state.startTime} تا ${state.endTime}'; } return InkWell( onTap: () async { final TimeOfDay? startTime = await showTimePicker( context: context, initialTime: TimeOfDay.now(), ); if (startTime == null) return; final TimeOfDay? endTime = await showTimePicker( context: context, initialTime: startTime, ); if (endTime == null) return; final formattedStartTime = '${startTime.hour.toString().padLeft(2, '0')}:${startTime.minute.toString().padLeft(2, '0')}'; final formattedEndTime = '${endTime.hour.toString().padLeft(2, '0')}:${endTime.minute.toString().padLeft(2, '0')}'; context.read().add( TimeRangeChanged( startTime: formattedStartTime, endTime: formattedEndTime, ), ); }, child: InputDecorator( decoration: _inputDecoration("بازه زمانی معتبر", isRequired: true), child: Text(displayText), ), ); }, ); } Widget _buildNotificationRadiusSlider() { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ IconButton( onPressed: () => showInfoDialog( context, title: "انتخاب محدوده نمایش تخفیف", content: "محدوده‌ای رو مشخص کن که تخفیف‌هات فقط به کاربرانی که تو اون شعاع هستن نشون داده بشه.", iconPath: Assets.icons.radar2, ), icon: SvgPicture.asset(Assets.icons.infoCircle, width: 17), ), Text( "شعاع ارسال اعلان تخفیف به مشتری‌ها", style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 14, ), ), ], ), ], ), BlocBuilder( builder: (context, state) { return Column( children: [ SliderTheme( data: SliderTheme.of(context).copyWith( activeTrackColor: AppColors.active, inactiveTrackColor: Colors.grey.shade300, trackShape: const RoundedRectSliderTrackShape(), trackHeight: 4.0, thumbColor: AppColors.active, thumbShape: const RoundSliderThumbShape( enabledThumbRadius: 12.0, ), overlayColor: AppColors.active.withAlpha(32), overlayShape: const RoundSliderOverlayShape( overlayRadius: 28.0, ), ), child: Slider( value: state.notificationRadius, min: 0, max: 1000, divisions: 100, label: '${state.notificationRadius.toInt()} متر', onChanged: (value) { context.read().add( NotificationRadiusChanged(value), ); }, ), ), SizedBox(height: 7,), BlocBuilder( builder: (context, state) { return Text( '${state.notificationRadius.toInt()} متر', style: const TextStyle( fontWeight: FontWeight.normal, fontSize: 14, color: Colors.black, ), ); }, ), ], ); }, ), ], ); } Widget _buildTextField({ required String label, String? hint, bool isRequired = false, int? maxLines, int? maxLength, TextInputType? keyboardType, required TextEditingController controller, ValueChanged? onChanged, }) { return ValueListenableBuilder( valueListenable: controller, builder: (context, value, child) { return TextFormField( controller: controller, onChanged: onChanged, maxLines: maxLines, maxLength: maxLength, keyboardType: keyboardType, decoration: _inputDecoration( label, hint: hint, isRequired: isRequired, ).copyWith( counterText: '', counter: maxLength != null ? Text( '${value.text.length}/$maxLength', style: Theme.of(context).textTheme.bodySmall, ) : null, ), ); }, ); } InputDecoration _inputDecoration( String label, { String? hint, bool isRequired = false, }) { return InputDecoration( hintText: hint, hintStyle: TextStyle( color: Color.fromARGB(255, 95, 95, 95), fontSize: 14, ), label: RichText( text: TextSpan( text: label, style: const TextStyle( color: Colors.black, fontFamily: 'Dana', fontSize: 18, fontWeight: FontWeight.bold, ), children: [ if (isRequired) const TextSpan(text: ' *', style: TextStyle(color: Colors.red)), ], ), ), ); } PreferredSizeWidget _buildCustomAppBar(BuildContext context) { return PreferredSize( preferredSize: const Size.fromHeight(70.0), child: Container( decoration: BoxDecoration( color: Colors.white, borderRadius: const BorderRadius.vertical( bottom: Radius.circular(15), ), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.08), blurRadius: 10, offset: const Offset(0, 4), ), ], ), child: SafeArea( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 10.0), child: Column( children: [ const SizedBox(height: 15), Row( children: [ Padding( padding: const EdgeInsets.only(right: 8), child: SvgPicture.asset(Assets.icons.logoWithName), ), const Spacer(), Row( children: [ IconButton( onPressed: () {}, icon: SvgPicture.asset( Assets.icons.discountShape, color: Colors.black, ), ), IconButton( onPressed: () {}, icon: SvgPicture.asset(Assets.icons.scanBarcode), ), ], ), ], ), ], ), ), ), ), ); } }