Houshan-Basa/lib/ui/screens/setting/my_account_page.dart

830 lines
37 KiB
Dart

// ignore_for_file: deprecated_member_use_from_same_package, use_build_context_synchronously
import 'dart:math';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_animate/flutter_animate.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:hoshan/core/gen/assets.gen.dart';
import 'package:hoshan/core/services/file_manager/excel_services.dart';
import 'package:hoshan/core/utils/date_time.dart';
import 'package:hoshan/data/model/ai/credit_model.dart';
import 'package:hoshan/data/model/empty_states_enum.dart';
import 'package:hoshan/ui/screens/setting/bloc/paymant_history_bloc.dart';
import 'package:hoshan/ui/screens/splash/cubit/user_info_cubit.dart';
import 'package:hoshan/ui/theme/colors.dart';
import 'package:hoshan/ui/theme/cubit/theme_mode_cubit.dart';
import 'package:hoshan/ui/theme/responsive.dart';
import 'package:hoshan/ui/theme/text.dart';
import 'package:hoshan/ui/widgets/components/button/circle_icon_btn.dart';
import 'package:hoshan/ui/widgets/components/button/loading_button.dart';
import 'package:hoshan/ui/widgets/components/dropdown/animated_setting_container.dart';
import 'package:hoshan/ui/widgets/components/snackbar/snackbar_manager.dart';
import 'package:hoshan/ui/widgets/sections/empty/empty_states.dart';
import 'package:hoshan/ui/widgets/sections/header/reversible_appbar.dart';
import 'package:hoshan/ui/widgets/sections/loading/default_placeholder.dart';
import 'package:open_filex/open_filex.dart';
import 'package:persian_number_utility/persian_number_utility.dart';
class MyAccountPage extends StatefulWidget {
const MyAccountPage({super.key});
@override
State<MyAccountPage> createState() => _MyAccountPageState();
}
class _MyAccountPageState extends State<MyAccountPage> {
List<DataColumn> dataColumns = [
DataColumn(
label: Text('ردیف',
style: AppTextStyles.body4.copyWith(color: AppColors.gray[700]))),
DataColumn(
label: Text('مبلغ',
style: AppTextStyles.body4.copyWith(color: AppColors.gray[700]))),
DataColumn(
label: Text('بابت',
style: AppTextStyles.body4.copyWith(color: AppColors.gray[700]))),
DataColumn(
label: Text('تاریخ',
style: AppTextStyles.body4.copyWith(color: AppColors.gray[700]))),
DataColumn(
label: Text('وضعیت',
style: AppTextStyles.body4.copyWith(color: AppColors.gray[700]))),
DataColumn(
label: Text('کدپیگیری',
style: AppTextStyles.body4.copyWith(color: AppColors.gray[700]))),
];
List<List<DataRow>> dataRows = [];
late final style = AppTextStyles.body4.copyWith(
color:
AppColors.gray[context.read<ThemeModeCubit>().isDark() ? 600 : 900]);
ScrollController scrollController = ScrollController();
@override
void initState() {
super.initState();
context.read<UserInfoCubit>().changeCredit(CreditModel(
credit: UserInfoCubit.userInfoModel.credit ?? 0,
freeCredit: UserInfoCubit.userInfoModel.freeCredit ?? 0));
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: ReversibleAppbar(
context,
titleText: 'حساب من',
),
body: Responsive(context).builder(
desktop: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
const SizedBox(
height: 24,
),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(child: history(context)),
Expanded(
child: Column(
children: [infoes(), faqs(context)],
),
),
],
),
],
),
),
mobile: SingleChildScrollView(
child: Column(
children: [
infoes(),
// Container(
// padding: const EdgeInsets.all(12),
// margin: const EdgeInsets.symmetric(horizontal: 16),
// decoration: BoxDecoration(
// color: Colors.white,
// borderRadius: BorderRadius.circular(16),
// ),
// child: Directionality(
// textDirection: TextDirection.rtl,
// child: Row(
// children: [
// CircleIconBtn(
// icon: Assets.icon.bulk.gift,
// size: 80,
// iconPadding: const EdgeInsets.all(12),
// color: AppColors.primaryColor.defaultShade,
// ),
// const SizedBox(
// width: 16,
// ),
// Expanded(
// child: Column(
// mainAxisAlignment: MainAxisAlignment.start,
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Text(
// 'امتیاز وفاداری',
// style: AppTextStyles.body3.copyWith(
// color: AppColors.primaryColor.defaultShade,
// fontWeight: FontWeight.bold),
// ),
// const SizedBox(
// height: 8,
// ),
// Text(
// 'معادل 10% تخفیف در خرید بعدی و یا شارژ 200 توکن برای ابزار تولید عکس',
// style: AppTextStyles.body4.copyWith(
// color: AppColors.black.defaultShade),
// ),
// const SizedBox(
// height: 8,
// ),
// Padding(
// padding: const EdgeInsets.only(left: 16.0),
// child: Row(
// mainAxisAlignment: MainAxisAlignment.end,
// children: [
// Container(
// padding:
// const EdgeInsets.fromLTRB(12, 4, 8, 4),
// decoration: BoxDecoration(
// color:
// AppColors.primaryColor.defaultShade,
// borderRadius: const BorderRadius.only(
// bottomRight: Radius.circular(4),
// topRight: Radius.circular(4))),
// child: Row(
// children: [
// Assets.icon.outline.documentCopy
// .svg(color: Colors.white),
// const SizedBox(
// width: 2,
// ),
// Text(
// 'کپی',
// style: AppTextStyles.body4
// .copyWith(color: Colors.white),
// ),
// ],
// ),
// ),
// Container(
// padding:
// const EdgeInsets.fromLTRB(8, 4, 12, 4),
// decoration: BoxDecoration(
// color: AppColors.gray[200],
// borderRadius: const BorderRadius.only(
// bottomLeft: Radius.circular(4),
// topLeft: Radius.circular(4))),
// child: Text(
// '2AZ5H',
// style: AppTextStyles.body4
// .copyWith(color: AppColors.gray[700]),
// ),
// ),
// ],
// ),
// )
// ],
// ),
// ),
// ],
// ),
// ),
// ),
// const SizedBox(
// height: 16,
// ),
history(context),
faqs(context)
],
),
),
),
);
}
Padding faqs(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(top: 24, bottom: 46),
child: Column(
children: [
AnimatedSettingContainer(
title: 'سکه‌ها توی این اپلیکیشن چی هستن؟',
icon: Icons.filter_1_rounded,
childrens: [
const SizedBox(
height: 24,
),
Text(
'سکه‌ها ارزهای مجازی داخل اپلیکیشن هوشان هستند که می‌تونی باهاشون قابلیت‌های ویژه‌ای رو فعال کنی. مثلاً ممکنه برای استفاده از امکانات پیشرفته‌تر یا درخواست‌های بیشتر، نیاز به خرج کردن سکه داشته باشی.',
textDirection: TextDirection.rtl,
style: AppTextStyles.body4.copyWith(
color: AppColors.gray[
context.read<ThemeModeCubit>().isDark() ? 600 : 900]),
),
const SizedBox(
height: 12,
),
]),
AnimatedSettingContainer(
title: 'چطوری می‌تونم سکه به دست بیارم؟',
icon: Icons.filter_2_rounded,
childrens: [
const SizedBox(
height: 24,
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(
'راه‌های مختلفی وجود داره:\n',
textDirection: TextDirection.rtl,
style: style,
),
],
),
Directionality(
textDirection: TextDirection.rtl,
child: Text.rich(TextSpan(style: style, children: [
TextSpan(
text: 'شارژ حساب: ',
style: style.copyWith(fontWeight: FontWeight.bold)),
TextSpan(
text:
'با ارتقای حساب کاربری، می‌تونی به میزان مصرفت، سکه بدست بیاری.\n\n',
style: style),
TextSpan(
text: 'کیف پول باسا: ',
style: style.copyWith(fontWeight: FontWeight.bold)),
TextSpan(
text:
'در بازه های زمانی مختلف کیف پول باسا شما شارژ میشه و میتونید از اعتبار آن استفاده کنید.',
style: style),
// TextSpan(
// text: 'هدیه روزانه: ',
// style: style.copyWith(fontWeight: FontWeight.bold)),
// TextSpan(
// text:
// 'هوشان هر روز 10 عدد سکه رایگان به عنوان هدیه بهت میده.',
// style: style),
])),
),
const SizedBox(
height: 12,
),
]),
// AnimatedSettingContainer(
// title:
// 'اگر از سکه‌های هدیه روزانه هوشان استفاده نکنم، در پایان روز از بین می‌روند؟',
// icon: Icons.filter_3_rounded,
// childrens: [
// const SizedBox(
// height: 24,
// ),
// Text(
// 'سکه‌ای که هر روز از هوشان دریافت می‌کنی، فقط تا پایان همون روز اعتبار داره. اگه ازش استفاده نکنی، منقضی می‌شه و به روز بعد منتقل نمی‌شه. پس بهتره قبل از تموم شدن روز ازش استفاده کنی!',
// textDirection: TextDirection.rtl,
// style: AppTextStyles.body4.copyWith(
// color: AppColors.gray[
// context.read<ThemeModeCubit>().isDark() ? 600 : 900]),
// ),
// const SizedBox(
// height: 12,
// ),
// ]),
// AnimatedSettingContainer(
// title:
// 'اگر سکه‌هام تموم بشه، دیگه نمی‌تونم از اپلیکیشن استفاده کنم؟',
// icon: Icons.filter_3_rounded,
// childrens: [
// const SizedBox(
// height: 24,
// ),
// Text(
// 'چرا میتونی ! برخی از امکانات پایه‌ای اپلیکیشن رایگان هستن. ولی برای دریافت امکانات پیشرفته یا درخواست‌های بیشتر باید سکه تهیه کنی. می‌تونی حسابت رو ارتقا بدی یا صبر کنی تا سکه رایگان روزانه دریافت کنی.',
// textDirection: TextDirection.rtl,
// style: AppTextStyles.body4.copyWith(
// color: AppColors.gray[
// context.read<ThemeModeCubit>().isDark() ? 600 : 900]),
// ),
// const SizedBox(
// height: 12,
// ),
// ]),
// AnimatedSettingContainer(
// title: 'چطور می‌تونم از طریق هوشان کسب درآمد کنم؟',
// icon: Icons.filter_4_rounded,
// childrens: [
// const SizedBox(
// height: 24,
// ),
// Text(
// 'در قسمت دستیارها، می‌تونی یک دستیار کاربردی بسازی و اون رو با دیگران به اشتراک بذاری. با هر بار استفاده از دستیارت، تو می‌تونی به درآمد برسی.',
// textDirection: TextDirection.rtl,
// style: AppTextStyles.body4.copyWith(
// color: AppColors.gray[
// context.read<ThemeModeCubit>().isDark() ? 600 : 900]),
// ),
// const SizedBox(
// height: 12,
// ),
// ]),
],
),
);
}
Column history(BuildContext context) {
return Column(
children: [
Padding(
padding: EdgeInsets.symmetric(
horizontal: Responsive(context).isMobile() ? 0 : 90.0),
child: Directionality(
textDirection: TextDirection.rtl,
child: ListTile(
title: Text(
'تاریخچه تراکنش‌ها',
style:
AppTextStyles.body3.copyWith(fontWeight: FontWeight.bold),
),
trailing: context.watch<PaymantHistoryBloc>().state
is PaymantHistorySuccess
? Row(
mainAxisSize: MainAxisSize.min,
children: [
CircleIconBtn(
color: context.read<ThemeModeCubit>().isDark()
? AppColors.black[900]
: AppColors.primaryColor[50],
iconColor: Theme.of(context).colorScheme.primary,
onTap: () {
if (context.read<PaymantHistoryBloc>().state
is PaymantHistorySuccess) {
final dc = [...dataColumns];
List<DataRow> dr = [];
for (var d in dataRows) {
dr.addAll(d);
}
dr = dr.map(
(e) {
final row = e;
row.cells.removeAt(4);
return row;
},
).toList();
dc.removeAt(4);
ExcelServices.saveDataTableToExcel(dc, dr).then(
(path) async {
if (path == null) {
SnackBarManager(context).show(
status: SnackBarStatus.error,
message: 'خطا در ذخیره سازی فایل');
} else {
try {
if (!kIsWeb) {
await ExcelServices.copyToDownloads(
path, path.split('/').last);
}
SnackBarManager(context).show(
status: SnackBarStatus.success,
message: 'فایل با موفقیت دانلود شد',
btns: GestureDetector(
onTap: () async {
try {
await OpenFilex.open(path);
} catch (e) {
if (kDebugMode) {
print(e);
}
}
},
child: Text(
'باز کردن فایل',
style: AppTextStyles.body5.copyWith(
color: AppColors
.black.defaultShade),
),
),
backgroundColor: AppColors.green[50],
);
} catch (e) {
SnackBarManager(context).show(
status: SnackBarStatus.error,
message: 'خطا در ذخیره سازی فایل');
}
}
},
);
}
},
icon: Assets.icon.outline.download,
),
const SizedBox(
width: 8,
),
CircleIconBtn(
color: context.read<ThemeModeCubit>().isDark()
? AppColors.black[900]
: AppColors.primaryColor[50],
iconColor: Theme.of(context).colorScheme.primary,
onTap: () => context
.read<PaymantHistoryBloc>()
.add(GetAllHistory()),
icon: Assets.icon.outline.bitcoinRefresh,
),
const SizedBox(
width: 8,
),
CircleIconBtn(
color: context.read<ThemeModeCubit>().isDark()
? AppColors.black[900]
: AppColors.primaryColor[50],
iconColor: Theme.of(context).colorScheme.primary,
onTap: () {
try {
scrollController.animateTo(
scrollController.position.minScrollExtent,
duration: 600.ms,
curve: Curves.easeIn);
} catch (e) {
if (kDebugMode) {
print('Error is: $e');
}
}
},
icon: Assets.icon.outline.arrowFlashRight),
const SizedBox(
width: 4,
),
Transform.rotate(
angle: pi,
child: CircleIconBtn(
color: context.read<ThemeModeCubit>().isDark()
? AppColors.black[900]
: AppColors.primaryColor[50],
iconColor:
Theme.of(context).colorScheme.primary,
onTap: () {
try {
scrollController.animateTo(
scrollController
.position.maxScrollExtent,
duration: 600.ms,
curve: Curves.easeIn);
} catch (e) {
if (kDebugMode) {
print('Error is: $e');
}
}
},
icon: Assets.icon.outline.arrowFlashRight)),
],
)
: null,
),
),
),
BlocBuilder<PaymantHistoryBloc, PaymantHistoryState>(
builder: (context, state) {
if (state is PaymantHistorySuccess) {
ValueNotifier<int> index = ValueNotifier(0);
dataRows = List.generate(
state.billings.length,
(page) => List.generate(
state.billings[page].billings.length,
(i) {
final billing = state.billings[page].billings[i];
return DataRow(
color: WidgetStateProperty.resolveWith<Color?>(
(Set<WidgetState> states) {
if (context.read<ThemeModeCubit>().state ==
ThemeMode.dark) {
return (i % 2 == 0)
? AppColors.gray[900]
: Theme.of(context).colorScheme.surface;
} else {
return (i % 2 == 0)
? AppColors.gray[200]
: Colors.white;
}
},
),
cells: [
DataCell(Text(((page * 5) + (i + 1)).toString(),
style: AppTextStyles.body4.copyWith(
color: Theme.of(context)
.colorScheme
.onSurface))),
DataCell(Text(
'${billing.amount.toString().seRagham()} تومان',
style: AppTextStyles.body4.copyWith(
color: Theme.of(context)
.colorScheme
.onSurface))),
// DataCell(Text(billing.amount.toString(),
// style: AppTextStyles.body4)),
DataCell(Text('${billing.credit} سکه',
style: AppTextStyles.body4.copyWith(
color: Theme.of(context)
.colorScheme
.onSurface))),
DataCell(Text(
DateTimeUtils.convertStringIsoToDate(
billing.createdAt ?? '')
.toPersianDate(),
style: AppTextStyles.body4.copyWith(
color: Theme.of(context)
.colorScheme
.onSurface))),
DataCell(Container(
padding: const EdgeInsets.symmetric(
horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: billing.status == 'success'
? AppColors.green.defaultShade
: billing.status == 'failed'
? AppColors.red.defaultShade
: Colors.orangeAccent,
borderRadius: BorderRadius.circular(4)),
child: Text(
billing.status == 'success'
? 'موفق'
: billing.status == 'failed'
? 'ناموفق'
: 'لغو شده',
style: AppTextStyles.body4.copyWith(
color: Colors.white,
fontWeight: FontWeight.bold),
),
)),
DataCell(Text(
billing.refId != null && billing.refId!.isNotEmpty
? billing.refId!
: '-',
style: AppTextStyles.body4.copyWith(
color: Theme.of(context)
.colorScheme
.onSurface))),
]);
},
),
);
return ValueListenableBuilder(
valueListenable: index,
builder: (context, selectedIndex, _) {
return Directionality(
textDirection: TextDirection.rtl,
child: Column(
children: [
SingleChildScrollView(
controller: scrollController,
scrollDirection: Axis.horizontal,
child: Container(
margin: const EdgeInsets.all(16),
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color:
Colors.black.withValues(alpha: 0.1),
offset: const Offset(0, 0),
blurRadius: 12,
spreadRadius: 0,
)
],
borderRadius: BorderRadius.circular(16),
color:
Theme.of(context).colorScheme.surface),
child: ClipRRect(
borderRadius: BorderRadius.circular(16),
child: DataTable(
columns: dataColumns,
rows: dataRows[selectedIndex]),
)),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Opacity(
opacity: index.value > 0 ? 1 : 0,
child: CircleIconBtn(
color:
context.read<ThemeModeCubit>().isDark()
? AppColors.black[900]
: AppColors.primaryColor[50],
iconColor:
Theme.of(context).colorScheme.primary,
onTap: () {
if (index.value > 0) {
index.value--;
}
},
icon: Assets.icon.outline.arrowRight),
),
const SizedBox(
width: 16,
),
Padding(
padding: const EdgeInsets.only(top: 4.0),
child: Text(
'${selectedIndex + 1}',
style: AppTextStyles.body3.copyWith(
color: Theme.of(context)
.colorScheme
.onSurface,
fontWeight: FontWeight.bold),
),
),
const SizedBox(
width: 16,
),
Opacity(
opacity:
index.value < dataRows.length - 1 ? 1 : 0,
child: Transform.rotate(
angle: pi,
child: CircleIconBtn(
color: context
.read<ThemeModeCubit>()
.isDark()
? AppColors.black[900]
: AppColors.primaryColor[50],
iconColor: Theme.of(context)
.colorScheme
.primary,
onTap: () {
if (index.value <
dataRows.length - 1) {
index.value++;
}
},
icon: Assets.icon.outline.arrowRight)),
),
],
)
],
),
);
});
}
return DefaultPlaceHolder(
enabled: (state is PaymantHistoryLoading ||
state is PaymantHistoryInitial),
child: Container(
width: MediaQuery.sizeOf(context).width,
height: state is PaymantHistoryEmpty ? null : 200,
margin: EdgeInsets.symmetric(
horizontal: Responsive(context).isMobile() ? 16 : 90.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
color: Theme.of(context).colorScheme.surface),
alignment: Alignment.center,
child: state is PaymantHistoryEmpty
? EmptyStates.getEmptyState(
scale: 0.8,
status: EmptyStatesEnum.amount,
title: 'تاریخچه‌ای وجود ندارد')
: null,
));
},
),
],
);
}
Padding infoes() {
return Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
BlocBuilder<UserInfoCubit, UserInfoState>(builder: (context, state) {
return Expanded(
child: cardInfo(
icon: Assets.icon.outline.coin,
title: 'اعتبار باقی مانده',
value:
'${((UserInfoCubit.userInfoModel.credit ?? 0) + (UserInfoCubit.userInfoModel.freeCredit ?? 0) + (UserInfoCubit.userInfoModel.gift_credit ?? 0))} سکه',
btnTitle: 'برای افزایش اعتبار به باسا مراجعه کنید',
onClick: () async {
// context.go(Routes.purchase);
},
),
);
}),
// const SizedBox(
// width: 16,
// ),
// BlocBuilder<UserInfoCubit, UserInfoState>(
// builder: (context, state) {
// return Expanded(
// child: cardInfo(
// icon: Assets.icon.outline.emptyWalletTick,
// title: 'درآمد',
// value: '${UserInfoCubit.userInfoModel.income ?? 0} سکه',
// btnTitle: 'تسویه حساب',
// onClick: () {
// DialogHandler(context: context).showCoin(
// onSuccess: () {
// final user = UserInfoCubit.userInfoModel;
// user.income = 0;
// context.read<UserInfoCubit>().changeUser(user);
// },
// );
// },
// ),
// );
// },
// )
],
),
);
}
Container cardInfo(
{required final SvgGenImage icon,
required final String title,
required final String value,
required final String btnTitle,
final Function()? onClick}) {
return Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(16),
),
child: Column(
children: [
const SizedBox(
height: 16,
),
CircleIconBtn(
size: 48,
iconPadding: const EdgeInsets.all(8),
icon: icon,
color: AppColors.secondryColor[50],
iconColor: AppColors.secondryColor.defaultShade,
),
const SizedBox(
height: 12,
),
Text(
title,
style: AppTextStyles.body4.copyWith(
fontWeight: FontWeight.bold,
color: Theme.of(context).colorScheme.onSurface),
),
const SizedBox(
height: 4,
),
Text(
value,
style: AppTextStyles.body4.copyWith(
color: AppColors
.gray[context.read<ThemeModeCubit>().isDark() ? 600 : 900]),
textDirection: TextDirection.rtl,
),
const SizedBox(
height: 12,
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: LoadingButton(
width: double.infinity,
color: context.read<ThemeModeCubit>().isDark()
? AppColors.black[900]
: AppColors.primaryColor[50],
onPressed: onClick,
radius: 8,
child: Text(
btnTitle,
textAlign: TextAlign.center,
style: AppTextStyles.body6
.copyWith(color: Theme.of(context).colorScheme.primary),
),
),
),
const SizedBox(
height: 12,
),
],
),
);
}
}