Files
kulakpos_app/lib/Screens/Home/home_screen.dart

501 lines
22 KiB
Dart
Raw Normal View History

2026-02-07 15:57:09 +07:00
import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:mobile_pos/Const/api_config.dart';
import 'package:mobile_pos/Repository/check_addon_providers.dart';
import 'package:mobile_pos/Screens/DashBoard/dashboard.dart';
import 'package:mobile_pos/Screens/Home/components/grid_items.dart';
import 'package:mobile_pos/Screens/Profile%20Screen/profile_details.dart';
import 'package:mobile_pos/core/theme/_app_colors.dart';
import 'package:mobile_pos/generated/l10n.dart' as lang;
import 'package:nb_utils/nb_utils.dart';
import 'package:restart_app/restart_app.dart';
import '../../Provider/profile_provider.dart';
import '../../constant.dart';
import '../../currency.dart';
import '../../service/check_actions_when_no_branch.dart';
import '../Customers/Provider/customer_provider.dart';
import '../DashBoard/global_container.dart';
import '../Home/Model/banner_model.dart' as b;
import '../../service/check_user_role_permission_provider.dart';
import '../branch/branch_list.dart';
import '../branch/repo/branch_repo.dart';
import '../subscription/package_screen.dart';
import 'Provider/banner_provider.dart';
class HomeScreen extends ConsumerStatefulWidget {
const HomeScreen({super.key});
@override
ConsumerState<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends ConsumerState<HomeScreen> {
PageController pageController = PageController(initialPage: 0, viewportFraction: 0.8);
bool _isRefreshing = false;
Future<void> refreshAllProviders({required WidgetRef ref}) async {
if (_isRefreshing) return; // Prevent multiple refresh calls
_isRefreshing = true;
try {
ref.refresh(summaryInfoProvider);
ref.refresh(bannerProvider);
ref.refresh(businessInfoProvider);
ref.refresh(partiesProvider);
ref.refresh(getExpireDateProvider(ref));
await Future.delayed(const Duration(seconds: 3));
} finally {
_isRefreshing = false;
}
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Consumer(builder: (_, ref, __) {
final businessInfo = ref.watch(businessInfoProvider);
final summaryInfo = ref.watch(summaryInfoProvider);
final banner = ref.watch(bannerProvider);
final permissionService = PermissionService(ref);
return businessInfo.when(data: (details) {
final icons = getFreeIcons(
context: context,
hrmPermission: (details.data?.addons?.hrmAddon == true),
brunchPermission: (((details.data?.addons?.multiBranchAddon == true) &&
(details.data?.enrolledPlan?.allowMultibranch == 1) &&
(details.data?.user?.branchId == null)))
? true
: false);
return Scaffold(
backgroundColor: kBackgroundColor,
appBar: AppBar(
backgroundColor: kWhite,
titleSpacing: 5,
surfaceTintColor: kWhite,
actions: [
if ((details.data?.addons?.multiBranchAddon ?? false) && (details.data?.user?.activeBranch != null))
TextButton.icon(
label: Text(
'${details.data?.user?.activeBranch?.name}',
style: theme.textTheme.bodyMedium?.copyWith(color: kTitleColor),
),
style: ButtonStyle(
shape: WidgetStatePropertyAll(
RoundedRectangleBorder(
borderRadius: BorderRadiusGeometry.circular(2),
),
),
textStyle: WidgetStatePropertyAll(
theme.textTheme.bodyMedium?.copyWith(color: kTitleColor),
),
),
onPressed: () async {
if (details.data?.user?.branchId != null) {
return;
}
bool switchBranch = await BranchListScreen.switchDialog(context: context, isLogin: false);
if (switchBranch) {
EasyLoading.show();
final switched =
await BranchRepo().exitBranch(id: details.data?.user?.activeBranchId.toString() ?? '');
if (switched) {
Restart.restartApp();
}
EasyLoading.dismiss();
}
},
icon: SvgPicture.asset(
'assets/branch_icon.svg',
height: 16,
width: 16,
),
),
IconButton(onPressed: () async => refreshAllProviders(ref: ref), icon: const Icon(Icons.refresh))
],
leading: Padding(
padding: const EdgeInsets.all(10),
child: GestureDetector(
onTap: () {
const ProfileDetails().launch(context);
},
child: Container(
height: 50,
width: 50,
decoration: details.data?.pictureUrl == null
? BoxDecoration(
image:
const DecorationImage(image: AssetImage('images/no_shop_image.png'), fit: BoxFit.cover),
borderRadius: BorderRadius.circular(50),
)
: BoxDecoration(
image: DecorationImage(
image: NetworkImage('${APIConfig.domain}${details.data?.pictureUrl}'),
fit: BoxFit.cover),
borderRadius: BorderRadius.circular(50),
),
),
),
),
title: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
details.data?.user?.role == 'staff'
? '${details.data?.companyName ?? ''} [${details.data?.user?.name ?? ''}]'
: details.data?.companyName ?? '',
style: theme.textTheme.titleLarge?.copyWith(
fontSize: 18.0,
fontWeight: FontWeight.w500,
),
),
GestureDetector(
// onTap: () {
// showDialog(
// context: context,
// builder: (BuildContext context) {
// return goToPackagePagePopup(
// context: context,
// enrolledPlan: details.enrolledPlan);
// });
// },
child: Text.rich(
TextSpan(
text: '${details.data?.enrolledPlan?.plan?.subscriptionName ?? 'No Active'} Plan',
children: [
// if (details.enrolledPlan?.duration != null &&
// details.enrolledPlan!.duration! <= 7)
// TextSpan(
// text: ' (${getDayLeftInExpiring(
// expireDate: details.willExpire,
// shortMSG: false,
// )})',
// style: theme.textTheme.bodySmall?.copyWith(
// fontSize: 13,
// color: kPeraColor,
// ),
// ),
],
),
style: theme.textTheme.bodySmall?.copyWith(
fontSize: 13,
color: kPeraColor,
fontWeight: FontWeight.w500,
)),
)
],
),
),
resizeToAvoidBottomInset: true,
body: RefreshIndicator.adaptive(
onRefresh: () async => refreshAllProviders(ref: ref),
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
if (permissionService.hasPermission(Permit.dashboardRead.value)) ...{
summaryInfo.when(data: (summary) {
return Container(
padding: EdgeInsets.fromLTRB(16, 16, 16, 12),
decoration: BoxDecoration(
color: kMainColor,
borderRadius: BorderRadius.circular(8),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(
child: Text(
lang.S.of(context).quickOver,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: theme.textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.w600,
fontSize: 18,
color: kWhite,
),
),
),
GestureDetector(
onTap: () => Navigator.push(
context, MaterialPageRoute(builder: (context) => DashboardScreen())),
child: Text(
lang.S.of(context).viewAll,
style: theme.textTheme.bodySmall?.copyWith(color: kWhite, fontSize: 16),
),
)
],
),
SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Flexible(
child: GlobalContainer(
minVerticalPadding: 0,
minTileHeight: 0,
titlePadding: EdgeInsets.zero,
// isShadow: true,
textColor: true,
title: lang.S.of(context).sales,
subtitle: '$currency${formatAmount(summary.data!.sales.toString())}',
),
),
Flexible(
child: GlobalContainer(
minVerticalPadding: 0,
minTileHeight: 0,
// isShadow: true,
textColor: true,
alainRight: true,
titlePadding: EdgeInsets.zero,
title: lang.S.of(context).purchased,
subtitle: '$currency${formatAmount(summary.data!.purchase.toString())}',
),
),
],
),
SizedBox(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Flexible(
child: GlobalContainer(
minVerticalPadding: 0,
textColor: true,
minTileHeight: 0,
titlePadding: EdgeInsets.zero,
title: lang.S.of(context).income,
subtitle: '$currency${formatAmount(summary.data!.income.toString())}',
),
),
Flexible(
child: GlobalContainer(
minVerticalPadding: 0,
minTileHeight: 0,
textColor: true,
alainRight: true,
titlePadding: EdgeInsets.zero,
title: lang.S.of(context).expense,
subtitle: '$currency${formatAmount(summary.data!.expense.toString())}',
),
),
],
),
],
),
);
}, error: (e, stack) {
return Text(e.toString());
}, loading: () {
return Center(
child: CircularProgressIndicator(),
);
}),
SizedBox(height: 16),
},
GridView.count(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
childAspectRatio: 3.0,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
crossAxisCount: 2,
children: List.generate(
icons.length,
(index) => HomeGridCards(
gridItems: icons[index],
),
),
),
const SizedBox(height: 20),
///________________Banner_______________________________________
banner.when(data: (imageData) {
List<b.Banner> images = [];
if (imageData.isNotEmpty) {
images.addAll(imageData.where(
(element) => element.status == 1,
));
}
if (images.isNotEmpty) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
lang.S.of(context).whatNew,
textAlign: TextAlign.start,
style: theme.textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.w600,
),
),
SizedBox(height: 12),
Container(
height: 150,
width: MediaQuery.of(context).size.width,
clipBehavior: Clip.antiAlias,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
),
child: ListView.builder(
scrollDirection: Axis.horizontal,
padding: EdgeInsets.zero,
itemCount: images.length,
itemBuilder: (_, index) {
return GestureDetector(
onTap: () {
const PackageScreen().launch(context);
},
child: Padding(
padding: EdgeInsetsDirectional.only(end: 10), // Spacing between items
child: ClipRRect(
borderRadius: BorderRadius.circular(5),
child: Image.network(
"${APIConfig.domain}${images[index].imageUrl}",
width: MediaQuery.of(context).size.width * 0.7, // 80% width
fit: BoxFit.cover,
),
),
),
);
},
),
),
const SizedBox(height: 12),
],
);
} else {
return Center(
child: Container(
height: 150,
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
image: DecorationImage(
fit: BoxFit.cover,
image: AssetImage('images/banner1.png'),
),
),
),
);
}
}, error: (e, stack) {
return Padding(
padding: const EdgeInsets.only(bottom: 20),
child: Center(
child: Text(
lang.S.of(context).noDataFound,
style: theme.textTheme.titleMedium,
//'No Data Found'
),
),
);
}, loading: () {
return const CircularProgressIndicator();
}),
],
),
),
),
));
}, error: (e, stack) {
return Center(child: Text(e.toString()));
}, loading: () {
return const Center(child: CircularProgressIndicator());
});
});
}
}
class HomeGridCards extends StatefulWidget {
const HomeGridCards({
super.key,
required this.gridItems,
// this.visibility,
});
final GridItems gridItems;
// final business.Visibility? visibility;
@override
State<HomeGridCards> createState() => _HomeGridCardsState();
}
class _HomeGridCardsState extends State<HomeGridCards> {
@override
Widget build(BuildContext context) {
return Consumer(builder: (context, ref, __) {
return GestureDetector(
onTap: () async {
bool result = await checkActionWhenNoBranch(context: context, actionName: widget.gridItems.title, ref: ref);
if (!result) {
return;
}
Navigator.of(context).pushNamed('/${widget.gridItems.route}');
},
child: Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(8), color: kWhite, boxShadow: [
BoxShadow(
color: const Color(0xff171717).withOpacity(0.07),
offset: const Offset(0, 3),
blurRadius: 50,
spreadRadius: -4)
]),
child: Row(
children: [
SvgPicture.asset(
widget.gridItems.icon.toString(),
height: 40,
width: 40,
),
const SizedBox(
width: 8,
),
Flexible(
child: Text(
widget.gridItems.title.toString(),
style: Theme.of(context).textTheme.bodyLarge?.copyWith(color: DAppColors.kNeutral700),
overflow: TextOverflow.ellipsis,
maxLines: 1,
))
],
),
),
);
});
}
}
String getSubscriptionExpiring({required String? expireDate, required bool shortMSG}) {
if (expireDate == null) {
return shortMSG ? 'N/A' : lang.S.current.subscribeNow;
}
DateTime expiringDay = DateTime.parse(expireDate).add(const Duration(days: 1));
if (expiringDay.isBefore(DateTime.now())) {
return lang.S.current.expired;
}
if (expiringDay.difference(DateTime.now()).inDays < 1) {
return shortMSG
? '${expiringDay.difference(DateTime.now()).inHours}\n${lang.S.current.hoursLeft}'
: '${expiringDay.difference(DateTime.now()).inHours} ${lang.S.current.hoursLeft}';
} else {
return shortMSG
? '${expiringDay.difference(DateTime.now()).inDays}\n${lang.S.current.daysLeft}'
: '${expiringDay.difference(DateTime.now()).inDays} ${lang.S.current.daysLeft}';
}
}