270 lines
9.0 KiB
Dart
270 lines
9.0 KiB
Dart
import 'dart:async';
|
||
import 'package:business_panel/core/config/app_colors.dart';
|
||
import 'package:business_panel/domain/entities/discount_entity.dart';
|
||
import 'package:business_panel/presentation/discount_management/bloc/discount_management_bloc.dart';
|
||
import 'package:business_panel/presentation/widgets/analytics_discount_card.dart';
|
||
import 'package:business_panel/presentation/widgets/custom_app_bar_single.dart';
|
||
import 'package:flutter/material.dart';
|
||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||
import 'package:flutter_svg/flutter_svg.dart';
|
||
import 'package:business_panel/gen/assets.gen.dart';
|
||
|
||
class DiscountManegmentPage extends StatelessWidget {
|
||
const DiscountManegmentPage({super.key});
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return BlocProvider(
|
||
create: (context) => DiscountManagementBloc(),
|
||
child: const _DiscountManegmentView(),
|
||
);
|
||
}
|
||
}
|
||
|
||
class _DiscountManegmentView extends StatefulWidget {
|
||
const _DiscountManegmentView();
|
||
|
||
@override
|
||
State<_DiscountManegmentView> createState() => _DiscountManegmentPageState();
|
||
}
|
||
|
||
class _DiscountManegmentPageState extends State<_DiscountManegmentView> {
|
||
final TextEditingController _searchController = TextEditingController();
|
||
Timer? _debounce;
|
||
int _selectedStatus = 1;
|
||
@override
|
||
void initState() {
|
||
super.initState();
|
||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||
if (mounted) {
|
||
context
|
||
.read<DiscountManagementBloc>()
|
||
.add(FetchManagedDiscounts(status: _selectedStatus));
|
||
}
|
||
});
|
||
}
|
||
|
||
@override
|
||
void dispose() {
|
||
_searchController.dispose();
|
||
_debounce?.cancel();
|
||
super.dispose();
|
||
}
|
||
|
||
void _onSearchChanged(String query) {
|
||
if (_debounce?.isActive ?? false) _debounce!.cancel();
|
||
_debounce = Timer(const Duration(milliseconds: 500), () {
|
||
if (mounted) {
|
||
context.read<DiscountManagementBloc>().add(
|
||
SearchManagedDiscounts(query: query, status: _selectedStatus));
|
||
}
|
||
});
|
||
}
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return Scaffold(
|
||
appBar: CustomAppBarSingle(
|
||
page: "تخفیف ها",
|
||
),
|
||
body: BlocListener<DiscountManagementBloc, DiscountManagementState>(
|
||
listener: (context, state) {
|
||
if (state is DiscountDeleteSuccess) {
|
||
ScaffoldMessenger.of(context)
|
||
..hideCurrentSnackBar()
|
||
..showSnackBar(
|
||
SnackBar(
|
||
content: Text(state.message),
|
||
backgroundColor: Colors.green),
|
||
);
|
||
context
|
||
.read<DiscountManagementBloc>()
|
||
.add(FetchManagedDiscounts(status: _selectedStatus));
|
||
} else if (state is DiscountDeleteFailure) {
|
||
ScaffoldMessenger.of(context)
|
||
..hideCurrentSnackBar()
|
||
..showSnackBar(
|
||
SnackBar(
|
||
content: Text(state.error), backgroundColor: Colors.red),
|
||
);
|
||
}
|
||
},
|
||
child: Column(
|
||
children: [
|
||
_buildSearchBar(),
|
||
_buildStatusFilters(),
|
||
_buildDiscountList(),
|
||
],
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
Widget _buildSearchBar() {
|
||
return Padding(
|
||
padding: const EdgeInsets.all(16.0),
|
||
child: TextField(
|
||
controller: _searchController,
|
||
decoration: InputDecoration(
|
||
hintText: 'دنبال چی میگردی؟',
|
||
hintStyle:
|
||
const TextStyle(color: Color.fromARGB(255, 157, 157, 157)),
|
||
prefixIcon: Padding(
|
||
padding: const EdgeInsets.all(12.0),
|
||
child: SvgPicture.asset(Assets.icons.riSearch2Line),
|
||
),
|
||
fillColor: const Color.fromARGB(255, 244, 244, 244),
|
||
filled: true,
|
||
border: OutlineInputBorder(
|
||
borderRadius: BorderRadius.circular(50),
|
||
borderSide: BorderSide.none,
|
||
),
|
||
contentPadding: const EdgeInsets.symmetric(vertical: 0),
|
||
),
|
||
onChanged: _onSearchChanged,
|
||
),
|
||
);
|
||
}
|
||
|
||
Widget _buildStatusFilters() {
|
||
return Padding(
|
||
padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0),
|
||
child: Row(
|
||
children: [
|
||
_buildStatusSelector(context, text: "تخفیفهای فعال", status: 1),
|
||
const SizedBox(width: 16),
|
||
_buildStatusSelector(context,
|
||
text: "تخفیفهای غیر فعال", status: 0),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
Widget _buildStatusSelector(BuildContext context,
|
||
{required String text, required int status}) {
|
||
final bool isSelected = _selectedStatus == status;
|
||
return Expanded(
|
||
child: InkWell(
|
||
onTap: () {
|
||
setState(() => _selectedStatus = status);
|
||
context
|
||
.read<DiscountManagementBloc>()
|
||
.add(FetchManagedDiscounts(status: _selectedStatus));
|
||
_searchController.clear();
|
||
},
|
||
borderRadius: BorderRadius.circular(50),
|
||
child: Padding(
|
||
padding: const EdgeInsets.symmetric(vertical: 12.0),
|
||
child: Row(
|
||
mainAxisAlignment: MainAxisAlignment.center,
|
||
children: [
|
||
Container(
|
||
width: 22,
|
||
height: 22,
|
||
decoration: BoxDecoration(
|
||
shape: BoxShape.circle,
|
||
color: isSelected ? Colors.white : Colors.transparent,
|
||
border: Border.all(
|
||
color:
|
||
isSelected ? AppColors.active : Colors.grey.shade400,
|
||
width: 2,
|
||
),
|
||
),
|
||
child: isSelected
|
||
? Center(
|
||
child: Container(
|
||
width: 12,
|
||
height: 12,
|
||
decoration: const BoxDecoration(
|
||
shape: BoxShape.circle,
|
||
color: AppColors.active,
|
||
),
|
||
),
|
||
)
|
||
: null,
|
||
),
|
||
const SizedBox(width: 10),
|
||
Text(
|
||
text,
|
||
style: const TextStyle(
|
||
color: AppColors.hint,
|
||
fontWeight: FontWeight.bold,
|
||
fontSize: 15),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
Widget _buildDiscountList() {
|
||
return Expanded(
|
||
child: BlocBuilder<DiscountManagementBloc, DiscountManagementState>(
|
||
builder: (context, state) {
|
||
if (state is DiscountManagementLoading) {
|
||
return const Center(child: CircularProgressIndicator());
|
||
}
|
||
if (state is DiscountManagementError) {
|
||
return Center(child: Text('خطا: ${state.message}'));
|
||
}
|
||
|
||
if (state is DiscountManagementLoaded) {
|
||
if (state.discounts.isEmpty) {
|
||
return const Center(
|
||
child: Text("هیچ تخفیفی با این مشخصات یافت نشد."));
|
||
}
|
||
final Map<String, List<DiscountEntity>> groupedDiscounts = {};
|
||
for (var discount in state.discounts) {
|
||
if (groupedDiscounts.containsKey(discount.type)) {
|
||
groupedDiscounts[discount.type]!.add(discount);
|
||
} else {
|
||
groupedDiscounts[discount.type] = [discount];
|
||
}
|
||
}
|
||
final groupKeys = groupedDiscounts.keys.toList();
|
||
|
||
return RefreshIndicator(
|
||
onRefresh: () async {
|
||
context
|
||
.read<DiscountManagementBloc>()
|
||
.add(FetchManagedDiscounts(status: _selectedStatus));
|
||
_searchController.clear();
|
||
},
|
||
child: ListView.builder(
|
||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||
itemCount: groupKeys.length,
|
||
itemBuilder: (context, index) {
|
||
final type = groupKeys[index];
|
||
final discountsOfType = groupedDiscounts[type]!;
|
||
return Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
Padding(
|
||
padding: const EdgeInsets.fromLTRB(0, 24, 0, 8),
|
||
child: Text(
|
||
"تخفیف $type",
|
||
style: const TextStyle(
|
||
fontSize: 18,
|
||
fontWeight: FontWeight.bold,
|
||
),
|
||
),
|
||
),
|
||
...discountsOfType.map((discount) {
|
||
return BlocProvider.value(
|
||
value: context.read<DiscountManagementBloc>(),
|
||
child: AnalyticsDiscountCard(discount: discount),
|
||
);
|
||
}).toList(),
|
||
],
|
||
);
|
||
},
|
||
),
|
||
);
|
||
}
|
||
return const Center(child: CircularProgressIndicator());
|
||
},
|
||
),
|
||
);
|
||
}
|
||
} |