496 lines
17 KiB
Dart
496 lines
17 KiB
Dart
import 'dart:io';
|
||
import 'package:business_panel/core/config/app_colors.dart';
|
||
import 'package:business_panel/gen/assets.gen.dart';
|
||
import 'package:business_panel/presentation/pages/working_hours_dialog.dart';
|
||
import 'package:business_panel/presentation/store_info/bloc/store_info_bloc.dart';
|
||
import 'package:business_panel/presentation/store_info/bloc/store_info_state.dart';
|
||
import 'package:flutter/material.dart';
|
||
import 'package:flutter/services.dart';
|
||
import 'package:flutter_svg/flutter_svg.dart';
|
||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||
import 'package:image_picker/image_picker.dart';
|
||
import 'package:business_panel/presentation/pages/osm_map_picker_page.dart'; // ایمپورت صفحه جدید
|
||
import 'package:latlong2/latlong.dart';
|
||
import 'package:persian_datetime_picker/persian_datetime_picker.dart';
|
||
|
||
class StoreInfoPage extends StatelessWidget {
|
||
const StoreInfoPage({super.key});
|
||
|
||
Future<void> _pickImage(BuildContext context) async {
|
||
try {
|
||
final ImagePicker picker = ImagePicker();
|
||
final XFile? image = await picker.pickImage(
|
||
source: ImageSource.gallery,
|
||
imageQuality: 80,
|
||
);
|
||
|
||
if (image != null && context.mounted) {
|
||
context.read<StoreInfoBloc>().add(StoreLogoChanged(image.path));
|
||
}
|
||
} on PlatformException catch (e) {
|
||
print("خطای دسترسی یا پلتفرم: ${e.message}");
|
||
} catch (e) {
|
||
print("یک خطای ناشناخته رخ داد: $e");
|
||
}
|
||
}
|
||
|
||
Future<void> _showWorkingHoursDialog(BuildContext context) async {
|
||
final result = await showDialog<Map<String, dynamic>>(
|
||
context: context,
|
||
builder: (context) => const WorkingHoursDialog(),
|
||
);
|
||
|
||
if (result != null && context.mounted) {
|
||
context.read<StoreInfoBloc>().add(
|
||
WorkingScheduleChanged(
|
||
days: result['days'] as List<String>,
|
||
startTime: result['startTime'] as String,
|
||
endTime: result['endTime'] as String,
|
||
),
|
||
);
|
||
}
|
||
}
|
||
|
||
// Future<void> _pickWorkingHours(BuildContext context) async {
|
||
// // ۱. انتخاب تاریخ شروع
|
||
// Jalali? startDate = await showPersianDatePicker(
|
||
// context: context,
|
||
// initialDate: Jalali.now(),
|
||
// firstDate: Jalali(1400),
|
||
// lastDate: Jalali(1405),
|
||
// );
|
||
// 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(1405),
|
||
// );
|
||
// if (endDate == null || !context.mounted) return;
|
||
|
||
// // ۴. انتخاب ساعت پایان
|
||
// TimeOfDay? endTime = await showTimePicker(
|
||
// context: context,
|
||
// initialTime: startTime,
|
||
// );
|
||
// if (endTime == null || !context.mounted) return;
|
||
|
||
// // ۵. تبدیل به آبجکت DateTime و ارسال به BLoC
|
||
// 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<StoreInfoBloc>().add(
|
||
// WorkingHoursChanged(
|
||
// startDateTime: startDateTime,
|
||
// endDateTime: endDateTime,
|
||
// ),
|
||
// );
|
||
// }
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return Scaffold(
|
||
appBar: _buildCustomAppBar(context),
|
||
body: SingleChildScrollView(
|
||
padding: const EdgeInsets.all(24.0),
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
const SizedBox(height: 16),
|
||
const Row(
|
||
children: [
|
||
Text(
|
||
"لوگوی فروشگاه",
|
||
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
|
||
),
|
||
SizedBox(width: 5),
|
||
Text("(اختیاری)", style: TextStyle(color: Colors.black54)),
|
||
],
|
||
),
|
||
const SizedBox(height: 24),
|
||
Center(
|
||
child: GestureDetector(
|
||
onTap: () => _pickImage(context),
|
||
child: CircleAvatar(
|
||
radius: 65,
|
||
backgroundColor: AppColors.uploadElevated,
|
||
child: BlocBuilder<StoreInfoBloc, StoreInfoState>(
|
||
builder: (context, state) {
|
||
return Container(
|
||
decoration: BoxDecoration(
|
||
shape: BoxShape.circle,
|
||
boxShadow: [
|
||
BoxShadow(
|
||
color: const Color.fromARGB(255, 224, 224, 224).withOpacity(0.5),
|
||
spreadRadius: 1,
|
||
blurRadius: 40,
|
||
offset: const Offset(0, 10),
|
||
),
|
||
],
|
||
image:
|
||
state.logoPath != null
|
||
? DecorationImage(
|
||
image: FileImage(File(state.logoPath!)),
|
||
fit: BoxFit.cover,
|
||
)
|
||
: null,
|
||
),
|
||
child: Stack(
|
||
alignment: Alignment.center,
|
||
children: [
|
||
Align(
|
||
alignment: Alignment.bottomRight,
|
||
child: CircleAvatar(
|
||
radius: 22,
|
||
backgroundColor: Colors.white,
|
||
child: CircleAvatar(
|
||
radius: 20,
|
||
backgroundColor: Colors.white,
|
||
child: SvgPicture.asset(
|
||
Assets.icons.edit02
|
||
),
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
},
|
||
),
|
||
),
|
||
),
|
||
),
|
||
const SizedBox(height: 32),
|
||
_buildSectionTitle(),
|
||
const SizedBox(height: 30),
|
||
_buildTextField(
|
||
label: "نام فروشگاه",
|
||
isRequired: true,
|
||
hint: "مثلاً کافه ایرونی",
|
||
),
|
||
const SizedBox(height: 30),
|
||
_buildActivityTypeDropdown(context),
|
||
const SizedBox(height: 30),
|
||
Row(
|
||
children: [
|
||
Expanded(
|
||
child: _buildTextField(label: "استان", hint: "اصفهان"),
|
||
),
|
||
const SizedBox(width: 16),
|
||
Expanded(child: _buildTextField(label: "شهر", hint: "اصفهان")),
|
||
],
|
||
),
|
||
const SizedBox(height: 30),
|
||
_buildTextField(
|
||
label: "جزئیات آدرس",
|
||
maxLines: 3,
|
||
hint: "خیابان، محله، ساختمان و ....",
|
||
),
|
||
const SizedBox(height: 30),
|
||
Row(
|
||
children: [
|
||
Expanded(child: _buildTextField(label: "پلاک")),
|
||
const SizedBox(width: 16),
|
||
Expanded(child: _buildTextField(label: "کد پستی")),
|
||
],
|
||
),
|
||
const SizedBox(height: 30),
|
||
Center(
|
||
child: TextButton.icon(
|
||
onPressed: () async {
|
||
final result = await Navigator.of(context).push<LatLng?>(
|
||
MaterialPageRoute(
|
||
builder: (context) => const OsmMapPickerPage(),
|
||
),
|
||
);
|
||
if (result != null) {
|
||
context.read<StoreInfoBloc>().add(
|
||
StoreLocationChanged(
|
||
latitude: result.latitude,
|
||
longitude: result.longitude,
|
||
),
|
||
);
|
||
}
|
||
},
|
||
icon: SvgPicture.asset(
|
||
Assets.icons.map,
|
||
color: AppColors.button,
|
||
height: 23,
|
||
),
|
||
label: const Text(
|
||
"انتخاب آدرس فروشگاه روی نقشه",
|
||
style: TextStyle(color: AppColors.button),
|
||
),
|
||
),
|
||
),
|
||
Center(
|
||
child: BlocBuilder<StoreInfoBloc, StoreInfoState>(
|
||
builder: (context, state) {
|
||
if (state.latitude != null && state.longitude != null) {
|
||
return Padding(
|
||
padding: const EdgeInsets.only(top: 8.0),
|
||
child: Text(
|
||
"آدرس با موفقیت ثبت شد ✓",
|
||
style: TextStyle(
|
||
color: Colors.green.shade700,
|
||
fontWeight: FontWeight.bold,
|
||
),
|
||
),
|
||
);
|
||
}
|
||
return const SizedBox.shrink();
|
||
},
|
||
),
|
||
),
|
||
const SizedBox(height: 30),
|
||
_buildTextField(
|
||
label: "تلفن تماس",
|
||
keyboardType: TextInputType.phone,
|
||
hint: "شماره تماس ثابت یا موبایل فروشگاه",
|
||
),
|
||
|
||
const SizedBox(height: 30),
|
||
_buildWorkingHoursPicker(context),
|
||
|
||
const SizedBox(height: 30),
|
||
_buildTextField(
|
||
label: "شماره جواز کسب",
|
||
hint: "شناسه صنفی 12 رقمی یکتا",
|
||
),
|
||
const SizedBox(height: 44),
|
||
SizedBox(
|
||
width: double.infinity,
|
||
child: ElevatedButton(
|
||
onPressed: () {},
|
||
child: const Text("تایید و ادامه"),
|
||
),
|
||
),
|
||
const SizedBox(height: 34),
|
||
],
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
Widget _buildSectionTitle() {
|
||
return const Text(
|
||
"ثبت مشخصات",
|
||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||
);
|
||
}
|
||
|
||
Widget _buildTextField({
|
||
required String label,
|
||
bool isRequired = false,
|
||
String? hint,
|
||
int maxLines = 1,
|
||
TextInputType? keyboardType,
|
||
}) {
|
||
return TextFormField(
|
||
maxLines: maxLines,
|
||
keyboardType: keyboardType,
|
||
decoration: InputDecoration(
|
||
hintText: hint,
|
||
hintStyle: const TextStyle(fontSize: 15, color: Colors.grey),
|
||
label: RichText(
|
||
text: TextSpan(
|
||
text: label,
|
||
style: const TextStyle(
|
||
color: Colors.black,
|
||
fontFamily: 'Dana',
|
||
fontSize: 19,
|
||
fontWeight: FontWeight.bold,
|
||
),
|
||
children: [
|
||
if (isRequired)
|
||
const TextSpan(
|
||
text: ' *',
|
||
style: TextStyle(color: Colors.red, fontSize: 16),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
Widget _buildWorkingHoursPicker(BuildContext context) {
|
||
return InkWell(
|
||
onTap: () => _showWorkingHoursDialog(context),
|
||
child: InputDecorator(
|
||
decoration: InputDecoration(
|
||
label: RichText(
|
||
text: const TextSpan(
|
||
text: "ساعت کار فروشگاه",
|
||
style: TextStyle(
|
||
color: Colors.black,
|
||
fontFamily: 'Dana',
|
||
fontSize: 19,
|
||
fontWeight: FontWeight.bold,
|
||
),
|
||
children: [
|
||
TextSpan(text: ' *', style: TextStyle(color: Colors.red, fontSize: 16)),
|
||
],
|
||
),
|
||
),
|
||
contentPadding: const EdgeInsets.symmetric(vertical: 18, horizontal: 12),
|
||
),
|
||
child: BlocBuilder<StoreInfoBloc, StoreInfoState>(
|
||
buildWhen: (p, c) => p.workingDays != c.workingDays,
|
||
builder: (context, state) {
|
||
final hasData = state.workingDays.isNotEmpty && state.startTime != null;
|
||
|
||
const Map<String, String> dayTranslations = {
|
||
'Saturday': 'شنبه', 'Sunday': 'یکشنبه', 'Monday': 'دوشنبه',
|
||
'Tuesday': 'سهشنبه', 'Wednesday': 'چهارشنبه', 'Thursday': 'پنجشنبه', 'Friday': 'جمعه'
|
||
};
|
||
final displayDays = state.workingDays.map((day) => dayTranslations[day] ?? '').join('، ');
|
||
|
||
String displayText = hasData
|
||
? "$displayDays\nاز ساعت ${state.startTime} تا ${state.endTime}"
|
||
: "انتخاب روز و ساعت کاری";
|
||
|
||
return Row(
|
||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||
children: [
|
||
Expanded(
|
||
child: Text(
|
||
displayText,
|
||
style: TextStyle(
|
||
fontSize: 16,
|
||
color: hasData ? Colors.black : Colors.grey.shade600,
|
||
height: 1.5,
|
||
),
|
||
textAlign: TextAlign.right,
|
||
),
|
||
),
|
||
],
|
||
);
|
||
},
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
|
||
Widget _buildActivityTypeDropdown(BuildContext context) {
|
||
final List<String> activityTypes = [
|
||
"🍔🍕 فست فود",
|
||
"👚👔 پوشاک",
|
||
"🍨🍹 تریا",
|
||
"📱📷 لوازم دیجیتال",
|
||
"🍣🍢 رستوران",
|
||
"☕🍰 کافی شاپ",
|
||
"👜👞 کیف و کفش",
|
||
"🎭🎟️ سینما",
|
||
"💄💅️ لوازم آرایشی",
|
||
"💍💎 طلا و زیورآلات",
|
||
];
|
||
|
||
return Theme(
|
||
data: Theme.of(context).copyWith(canvasColor: const Color(0xFFF6F6F6)),
|
||
child: DropdownButtonFormField<String>(
|
||
icon: SvgPicture.asset(Assets.icons.arrowDown, width: 24,color: Colors.black,),
|
||
decoration: InputDecoration(
|
||
label: RichText(
|
||
text: const TextSpan(
|
||
text: "نوع فعالیت",
|
||
style: TextStyle(
|
||
color: Colors.black,
|
||
fontFamily: 'Dana',
|
||
fontSize: 19,
|
||
fontWeight: FontWeight.bold,
|
||
),
|
||
children: [
|
||
TextSpan(
|
||
text: ' *',
|
||
style: TextStyle(color: Colors.red, fontSize: 16),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
borderRadius: BorderRadius.circular(12.0),
|
||
isExpanded: true,
|
||
items:
|
||
activityTypes.map((String value) {
|
||
return DropdownMenuItem<String>(value: value, child: Text(value));
|
||
}).toList(),
|
||
onChanged: (value) {
|
||
if (value != null) {
|
||
context.read<StoreInfoBloc>().add(ActivityTypeChanged(value));
|
||
}
|
||
},
|
||
),
|
||
);
|
||
}
|
||
}
|
||
|
||
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),
|
||
),
|
||
],
|
||
),
|
||
],
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
),
|
||
);
|
||
}
|