first commit
This commit is contained in:
234
lib/Screens/branch/add_and_edit_brunch_screen.dart
Normal file
234
lib/Screens/branch/add_and_edit_brunch_screen.dart
Normal file
@@ -0,0 +1,234 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:mobile_pos/Screens/branch/repo/branch_repo.dart';
|
||||
import 'package:mobile_pos/generated/l10n.dart' as l;
|
||||
import '../../GlobalComponents/glonal_popup.dart';
|
||||
import '../../constant.dart';
|
||||
import '../../service/check_user_role_permission_provider.dart';
|
||||
import 'model/branch_list_model.dart';
|
||||
|
||||
class AddAndEditBranch extends StatefulWidget {
|
||||
final BranchData? branchData;
|
||||
|
||||
const AddAndEditBranch({super.key, this.branchData});
|
||||
|
||||
@override
|
||||
_AddAndEditBranchState createState() => _AddAndEditBranchState();
|
||||
}
|
||||
|
||||
class _AddAndEditBranchState extends State<AddAndEditBranch> {
|
||||
final formKey = GlobalKey<FormState>();
|
||||
|
||||
final nameController = TextEditingController();
|
||||
final phoneController = TextEditingController();
|
||||
final emailController = TextEditingController();
|
||||
final addressController = TextEditingController();
|
||||
final openingBalanceController = TextEditingController();
|
||||
final descriptionController = TextEditingController();
|
||||
|
||||
bool get isEdit => widget.branchData != null;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (isEdit) {
|
||||
nameController.text = widget.branchData?.name ?? '';
|
||||
phoneController.text = widget.branchData?.phone ?? '';
|
||||
emailController.text = widget.branchData?.email ?? '';
|
||||
addressController.text = widget.branchData?.address ?? '';
|
||||
openingBalanceController.text = widget.branchData?.branchOpeningBalance?.toString() ?? '';
|
||||
descriptionController.text = widget.branchData?.description ?? '';
|
||||
}
|
||||
}
|
||||
|
||||
bool validateAndSave() {
|
||||
final form = formKey.currentState;
|
||||
if (form!.validate()) {
|
||||
form.save();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void resetForm() {
|
||||
nameController.clear();
|
||||
phoneController.clear();
|
||||
emailController.clear();
|
||||
addressController.clear();
|
||||
openingBalanceController.clear();
|
||||
descriptionController.clear();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final _lang = l.S.of(context);
|
||||
return Consumer(builder: (context, ref, __) {
|
||||
final permissionService = PermissionService(ref);
|
||||
return GlobalPopup(
|
||||
child: Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
appBar: AppBar(
|
||||
title: Text(isEdit ? _lang.updateBranch : _lang.createBranch),
|
||||
centerTitle: true,
|
||||
backgroundColor: Colors.white,
|
||||
iconTheme: const IconThemeData(color: Colors.black),
|
||||
elevation: 0,
|
||||
),
|
||||
body: Form(
|
||||
key: formKey,
|
||||
child: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(right: 10.0, left: 10, top: 20, bottom: 10),
|
||||
child: Column(
|
||||
spacing: 16,
|
||||
children: [
|
||||
TextFormField(
|
||||
controller: nameController,
|
||||
validator: (v) => v!.isEmpty ? _lang.pleaseEnterBranchName : null,
|
||||
decoration: kInputDecoration.copyWith(
|
||||
labelText: _lang.name,
|
||||
hintText: _lang.enterName,
|
||||
),
|
||||
),
|
||||
|
||||
TextFormField(
|
||||
controller: phoneController,
|
||||
keyboardType: TextInputType.phone,
|
||||
decoration: kInputDecoration.copyWith(
|
||||
labelText: _lang.phone,
|
||||
hintText: _lang.enterYourPhoneNumber,
|
||||
),
|
||||
),
|
||||
|
||||
TextFormField(
|
||||
controller: emailController,
|
||||
decoration: kInputDecoration.copyWith(
|
||||
labelText: _lang.email,
|
||||
hintText: _lang.enterEmail,
|
||||
),
|
||||
),
|
||||
|
||||
TextFormField(
|
||||
controller: addressController,
|
||||
decoration: kInputDecoration.copyWith(
|
||||
labelText: _lang.address,
|
||||
hintText: _lang.enterAddress,
|
||||
),
|
||||
),
|
||||
|
||||
TextFormField(
|
||||
controller: openingBalanceController,
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
|
||||
decoration: kInputDecoration.copyWith(
|
||||
labelText: _lang.openingBalance,
|
||||
hintText: _lang.enterBalance,
|
||||
),
|
||||
),
|
||||
|
||||
TextFormField(
|
||||
controller: descriptionController,
|
||||
maxLines: 3,
|
||||
decoration: kInputDecoration.copyWith(
|
||||
labelText: _lang.description,
|
||||
hintText: _lang.enterDescription,
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
vertical: 16,
|
||||
horizontal: 12,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
/// Buttons
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 10.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Expanded(
|
||||
child: OutlinedButton(
|
||||
onPressed: resetForm,
|
||||
style: OutlinedButton.styleFrom(
|
||||
side: const BorderSide(color: kMainColor),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
|
||||
),
|
||||
child: Text(_lang.resets, style: TextStyle(color: kMainColor)),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: kMainColor,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 12),
|
||||
),
|
||||
onPressed: () async {
|
||||
if (validateAndSave()) {
|
||||
if (isEdit) {
|
||||
if (!permissionService.hasPermission(Permit.branchesUpdate.value)) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
_lang.youDoNotHavePermissionToUpdateBranch,
|
||||
),
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
EasyLoading.show();
|
||||
await BranchRepo().updateBranch(
|
||||
ref: ref,
|
||||
context: context,
|
||||
id: widget.branchData!.id.toString(),
|
||||
name: nameController.text,
|
||||
phone: phoneController.text,
|
||||
email: emailController.text,
|
||||
address: addressController.text,
|
||||
branchOpeningBalance: openingBalanceController.text,
|
||||
description: descriptionController.text,
|
||||
);
|
||||
} else {
|
||||
// 🔹 Add Mode
|
||||
if (!permissionService.hasPermission(Permit.branchesCreate.value)) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(_lang.youDoNotHavePermissionToUpdateBranch)),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
EasyLoading.show();
|
||||
await BranchRepo().createBranch(
|
||||
ref: ref,
|
||||
context: context,
|
||||
name: nameController.text,
|
||||
phone: phoneController.text,
|
||||
email: emailController.text,
|
||||
address: addressController.text,
|
||||
branchOpeningBalance: openingBalanceController.text,
|
||||
description: descriptionController.text,
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
isEdit ? _lang.update : _lang.save,
|
||||
style: const TextStyle(color: Colors.white),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
525
lib/Screens/branch/branch_list.dart
Normal file
525
lib/Screens/branch/branch_list.dart
Normal file
@@ -0,0 +1,525 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:mobile_pos/Screens/branch/provider/branch_list_provider.dart';
|
||||
import 'package:mobile_pos/Screens/branch/repo/branch_repo.dart';
|
||||
import 'package:mobile_pos/constant.dart';
|
||||
import 'package:restart_app/restart_app.dart';
|
||||
import 'package:mobile_pos/generated/l10n.dart' as l;
|
||||
import '../../Provider/profile_provider.dart';
|
||||
import '../../widgets/empty_widget/_empty_widget.dart';
|
||||
import '../../widgets/key_values/key_values_widget.dart';
|
||||
import '../../service/check_user_role_permission_provider.dart';
|
||||
import 'add_and_edit_brunch_screen.dart';
|
||||
|
||||
class BranchListScreen extends ConsumerStatefulWidget {
|
||||
const BranchListScreen({super.key});
|
||||
static Future<bool> switchDialog({required BuildContext context, required bool isLogin}) async {
|
||||
const Color primaryColor = Color(0xffC52127);
|
||||
|
||||
return await showDialog<bool>(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (BuildContext context) {
|
||||
return Dialog(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
elevation: 8,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(24.0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
CircleAvatar(
|
||||
backgroundColor: primaryColor.withOpacity(0.1),
|
||||
radius: 30,
|
||||
child: Icon(
|
||||
Icons.sync_alt_rounded,
|
||||
color: primaryColor,
|
||||
size: 32,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Text(
|
||||
isLogin ? l.S.of(context).switchBank : l.S.of(context).exitBank,
|
||||
style: const TextStyle(
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
isLogin
|
||||
? l.S.of(context).areYouSureWantToSwitchToDifferentBranch
|
||||
: l.S.of(context).areYourSureYouWantToExitFromThisBranch,
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
color: Colors.black87,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: OutlinedButton(
|
||||
style: OutlinedButton.styleFrom(
|
||||
side: const BorderSide(color: kMainColor),
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(false);
|
||||
},
|
||||
child: Text(
|
||||
l.S.of(context).cancel,
|
||||
style: TextStyle(fontSize: 16),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: primaryColor,
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
elevation: 2,
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(true);
|
||||
},
|
||||
child: Text(
|
||||
isLogin ? l.S.of(context).switchs : l.S.of(context).exit,
|
||||
style: const TextStyle(fontSize: 16, color: Colors.white),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
) ??
|
||||
false;
|
||||
}
|
||||
|
||||
@override
|
||||
ConsumerState<BranchListScreen> createState() => _BranchListScreenState();
|
||||
}
|
||||
|
||||
class _BranchListScreenState extends ConsumerState<BranchListScreen> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final _lang = l.S.of(context);
|
||||
final _theme = Theme.of(context);
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
appBar: AppBar(
|
||||
backgroundColor: Colors.white,
|
||||
centerTitle: true,
|
||||
title: Text(
|
||||
_lang.branchList,
|
||||
style: _theme.textTheme.bodyMedium?.copyWith(
|
||||
color: kTitleColor,
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
bottom: const PreferredSize(
|
||||
preferredSize: Size.fromHeight(1),
|
||||
child: Divider(
|
||||
height: 1,
|
||||
color: Color(0xFFE8E9F2),
|
||||
),
|
||||
),
|
||||
),
|
||||
bottomNavigationBar: Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: ElevatedButton.icon(
|
||||
iconAlignment: IconAlignment.end,
|
||||
onPressed: () async {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => AddAndEditBranch(),
|
||||
));
|
||||
},
|
||||
label: Text(_lang.createBranch),
|
||||
),
|
||||
),
|
||||
body: const BranchListWidget(formFullPage: true),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class BranchListWidget extends ConsumerWidget {
|
||||
const BranchListWidget({required this.formFullPage, super.key});
|
||||
|
||||
final bool formFullPage;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final _theme = Theme.of(context);
|
||||
final branchList = ref.watch(branchListProvider);
|
||||
final profile = ref.watch(businessInfoProvider);
|
||||
final permissionService = PermissionService(ref);
|
||||
|
||||
return branchList.when(
|
||||
data: (snapshot) {
|
||||
if (!permissionService.hasPermission(Permit.branchesRead.value)) {
|
||||
return const Center(child: PermitDenyWidget());
|
||||
}
|
||||
return profile.when(
|
||||
data: (profileSnap) {
|
||||
final activeBranchId = profileSnap.data?.user?.activeBranchId;
|
||||
return RefreshIndicator.adaptive(
|
||||
onRefresh: () async {
|
||||
ref.refresh(branchListProvider);
|
||||
ref.refresh(businessInfoProvider);
|
||||
},
|
||||
child: snapshot.data?.isNotEmpty ?? false
|
||||
? ListView.separated(
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
shrinkWrap: true,
|
||||
itemCount: snapshot.data?.length ?? 0,
|
||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
itemBuilder: (context, index) {
|
||||
final branch = snapshot.data?[index];
|
||||
final isActiveBranch = branch?.id == activeBranchId;
|
||||
|
||||
Future<void> _handleMenuAction(String value) async {
|
||||
switch (value) {
|
||||
case 'view':
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(24)),
|
||||
),
|
||||
builder: (context) => _buildViewDetailsSheet(context, _theme, branch),
|
||||
);
|
||||
break;
|
||||
case 'edit':
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => AddAndEditBranch(
|
||||
branchData: branch,
|
||||
),
|
||||
));
|
||||
break;
|
||||
case 'delete':
|
||||
showDialog(
|
||||
barrierDismissible: false,
|
||||
context: context,
|
||||
builder: (BuildContext dialogContext) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Center(
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(16),
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(8),
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
l.S.of(context).areYourSureYouWantToExitFromThisBranch,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 26),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Color(0xffF68A3D).withValues(alpha: 0.1),
|
||||
),
|
||||
padding: EdgeInsets.all(20),
|
||||
child: SvgPicture.asset(
|
||||
height: 126,
|
||||
width: 126,
|
||||
'images/trash.svg',
|
||||
),
|
||||
),
|
||||
SizedBox(height: 26),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: OutlinedButton(
|
||||
onPressed: () async {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Text(l.S.of(context).cancel),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: () async {
|
||||
await Future.delayed(Duration.zero);
|
||||
BranchRepo repo = BranchRepo();
|
||||
bool success;
|
||||
success = await repo.deleteUser(
|
||||
id: branch?.id.toString() ?? '', context: context, ref: ref);
|
||||
if (success) {
|
||||
ref.refresh(branchListProvider);
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
||||
content: Text(l.S.of(context).deletedSuccessFully)));
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
child: Text(l.S.of(context).delete),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
break;
|
||||
|
||||
case 'login':
|
||||
bool switchBranch = await BranchListScreen.switchDialog(
|
||||
context: context,
|
||||
isLogin: true,
|
||||
);
|
||||
|
||||
if (switchBranch) {
|
||||
EasyLoading.show();
|
||||
|
||||
final switched = await BranchRepo().switchBranch(id: branch?.id.toString() ?? '');
|
||||
|
||||
if (switched) {
|
||||
ref.refresh(branchListProvider);
|
||||
ref.refresh(businessInfoProvider);
|
||||
Restart.restartApp();
|
||||
}
|
||||
EasyLoading.dismiss();
|
||||
}
|
||||
break;
|
||||
|
||||
case 'exit':
|
||||
bool exitBranch = await BranchListScreen.switchDialog(
|
||||
context: context,
|
||||
isLogin: false,
|
||||
);
|
||||
|
||||
if (exitBranch) {
|
||||
EasyLoading.show();
|
||||
|
||||
final switched = await BranchRepo().exitBranch(id: branch?.id.toString() ?? '');
|
||||
|
||||
if (switched) {
|
||||
ref.refresh(branchListProvider);
|
||||
ref.refresh(businessInfoProvider);
|
||||
Restart.restartApp();
|
||||
}
|
||||
EasyLoading.dismiss();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
debugPrint('Unknown menu action: $value');
|
||||
}
|
||||
}
|
||||
|
||||
return ListTile(
|
||||
onTap: () async {
|
||||
await _handleMenuAction(isActiveBranch ? 'exit' : 'login');
|
||||
},
|
||||
visualDensity: const VisualDensity(horizontal: -4, vertical: -2),
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
title: Row(
|
||||
children: [
|
||||
Text(
|
||||
branch?.name?.toString() ?? 'n/a',
|
||||
style: _theme.textTheme.bodyMedium?.copyWith(
|
||||
color: kTitleColor,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
if (isActiveBranch) ...[
|
||||
const SizedBox(width: 6),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 2.5),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(2),
|
||||
color: const Color(0xff08B935).withOpacity(0.12),
|
||||
),
|
||||
child: Text(
|
||||
l.S.of(context).currents,
|
||||
style: _theme.textTheme.bodyMedium?.copyWith(
|
||||
color: const Color(0xff00A92B),
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
]
|
||||
],
|
||||
),
|
||||
subtitle: Text(
|
||||
branch?.address?.toString() ?? 'n/a',
|
||||
style: _theme.textTheme.bodyMedium?.copyWith(
|
||||
color: const Color(0xff4B5563),
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
trailing: PopupMenuButton<String>(
|
||||
icon: const Icon(
|
||||
Icons.more_vert,
|
||||
color: Color(0xff4B5563),
|
||||
),
|
||||
onSelected: _handleMenuAction,
|
||||
itemBuilder: (context) => [
|
||||
PopupMenuItem<String>(
|
||||
value: 'view',
|
||||
child: Text(l.S.of(context).view),
|
||||
),
|
||||
if (PermissionService(ref).hasPermission(Permit.branchesUpdate.value))
|
||||
PopupMenuItem<String>(
|
||||
value: 'edit',
|
||||
child: Text(l.S.of(context).edit),
|
||||
),
|
||||
if (PermissionService(ref).hasPermission(Permit.branchesDelete.value))
|
||||
PopupMenuItem<String>(
|
||||
value: 'delete',
|
||||
child: Text(l.S.of(context).delete),
|
||||
),
|
||||
// You can uncomment these if you want to enable login/exit from menu
|
||||
// PopupMenuItem<String>(
|
||||
// value: 'login',
|
||||
// child: Text('Login'),
|
||||
// ),
|
||||
// PopupMenuItem<String>(
|
||||
// value: 'exit',
|
||||
// child: Text('Exit'),
|
||||
// ),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
separatorBuilder: (context, index) => const Divider(
|
||||
height: 0,
|
||||
color: Color(0xffDADADA),
|
||||
),
|
||||
)
|
||||
: EmptyWidget(
|
||||
message: TextSpan(text: l.S.of(context).noBrunchFound),
|
||||
),
|
||||
);
|
||||
},
|
||||
error: (e, stack) => Center(child: Text(e.toString())),
|
||||
loading: () => const Center(child: CircularProgressIndicator()),
|
||||
);
|
||||
},
|
||||
error: (e, stack) => Center(child: Text(e.toString())),
|
||||
loading: () => formFullPage
|
||||
? const Center(child: SizedBox(height: 40, width: 40, child: CircularProgressIndicator()))
|
||||
: Container(
|
||||
height: 100,
|
||||
width: MediaQuery.of(context).size.width,
|
||||
decoration: BoxDecoration(color: Colors.transparent),
|
||||
child: const Center(child: SizedBox(height: 40, width: 40, child: CircularProgressIndicator()))),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildViewDetailsSheet(BuildContext context, ThemeData theme, dynamic branch) {
|
||||
return Container(
|
||||
width: double.maxFinite,
|
||||
decoration: const BoxDecoration(
|
||||
borderRadius: BorderRadius.vertical(
|
||||
top: Radius.circular(24),
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
l.S.of(context).viewDetails,
|
||||
style: theme.textTheme.titleMedium?.copyWith(
|
||||
color: const Color(0xff121535),
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 18,
|
||||
),
|
||||
),
|
||||
const CloseButton(),
|
||||
],
|
||||
),
|
||||
),
|
||||
const Divider(
|
||||
height: 0,
|
||||
color: Color(0xffE6E6E6),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
|
||||
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
|
||||
...{
|
||||
l.S.of(context).name: branch?.name?.toString() ?? 'n/a',
|
||||
l.S.of(context).phone: branch?.phone?.toString() ?? 'n/a',
|
||||
l.S.of(context).email: branch?.email?.toString() ?? 'n/a',
|
||||
l.S.of(context).address: branch?.address?.toString() ?? 'n/a',
|
||||
}.entries.map(
|
||||
(entry) {
|
||||
return KeyValueRow(
|
||||
title: entry.key,
|
||||
titleFlex: 1,
|
||||
description: entry.value.toString(),
|
||||
descriptionFlex: 4,
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
l.S.of(context).description,
|
||||
style: theme.textTheme.bodyMedium?.copyWith(
|
||||
color: kTitleColor,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
branch?.description?.toString() ?? 'n/a',
|
||||
style: theme.textTheme.bodyMedium?.copyWith(
|
||||
color: const Color(0xff4B5563),
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
]),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
135
lib/Screens/branch/branch_screen.dart
Normal file
135
lib/Screens/branch/branch_screen.dart
Normal file
@@ -0,0 +1,135 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:mobile_pos/Screens/Report/reports.dart';
|
||||
import 'package:mobile_pos/Screens/User%20Roles/user_role_screen.dart';
|
||||
import 'package:mobile_pos/constant.dart';
|
||||
import 'package:mobile_pos/generated/l10n.dart' as l;
|
||||
import 'branch_list.dart';
|
||||
|
||||
class BranchScreen extends StatefulWidget {
|
||||
const BranchScreen({super.key});
|
||||
|
||||
@override
|
||||
State<BranchScreen> createState() => _BranchScreenState();
|
||||
}
|
||||
|
||||
class _BranchScreenState extends State<BranchScreen> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final _lang = l.S.of(context);
|
||||
final _theme = Theme.of(context);
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
appBar: AppBar(
|
||||
backgroundColor: Colors.white,
|
||||
centerTitle: true,
|
||||
title: Text(
|
||||
_lang.branch,
|
||||
style: _theme.textTheme.bodyMedium?.copyWith(
|
||||
color: kTitleColor,
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
bottom: PreferredSize(
|
||||
preferredSize: const Size.fromHeight(1),
|
||||
child: Container(
|
||||
color: Color(0xFFE8E9F2),
|
||||
height: 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 16),
|
||||
child: Column(
|
||||
children: [
|
||||
ListTile(
|
||||
onTap: () => Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => BranchListScreen(),
|
||||
),
|
||||
),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
leading: SvgPicture.asset(
|
||||
'assets/branch_list.svg',
|
||||
height: 36,
|
||||
width: 36,
|
||||
),
|
||||
title: Text(
|
||||
_lang.branchList,
|
||||
style: _theme.textTheme.bodyMedium?.copyWith(
|
||||
color: kTitleColor,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
trailing: Icon(
|
||||
Icons.keyboard_arrow_right_rounded,
|
||||
color: Color(0xff4B5563),
|
||||
),
|
||||
),
|
||||
Divider(
|
||||
color: Color(0xffE6E6E6),
|
||||
),
|
||||
ListTile(
|
||||
onTap: () => Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => UserRoleScreen(),
|
||||
),
|
||||
),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
leading: SvgPicture.asset(
|
||||
'assets/role_permission.svg',
|
||||
height: 36,
|
||||
width: 36,
|
||||
),
|
||||
title: Text(
|
||||
_lang.roleAndPermission,
|
||||
style: _theme.textTheme.bodyMedium?.copyWith(
|
||||
color: kTitleColor,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
trailing: Icon(
|
||||
Icons.keyboard_arrow_right_rounded,
|
||||
color: Color(0xff4B5563),
|
||||
),
|
||||
),
|
||||
Divider(
|
||||
color: Color(0xffE6E6E6),
|
||||
),
|
||||
ListTile(
|
||||
onTap: () => Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => Reports(),
|
||||
),
|
||||
),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
leading: SvgPicture.asset(
|
||||
'assets/report.svg',
|
||||
height: 36,
|
||||
width: 36,
|
||||
),
|
||||
title: Text(
|
||||
_lang.reports,
|
||||
style: _theme.textTheme.bodyMedium?.copyWith(
|
||||
color: kTitleColor,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
trailing: Icon(
|
||||
Icons.keyboard_arrow_right_rounded,
|
||||
color: Color(0xff4B5563),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
96
lib/Screens/branch/model/branch_list_model.dart
Normal file
96
lib/Screens/branch/model/branch_list_model.dart
Normal file
@@ -0,0 +1,96 @@
|
||||
class BranchListModel {
|
||||
BranchListModel({
|
||||
this.message,
|
||||
this.data,
|
||||
});
|
||||
|
||||
BranchListModel.fromJson(dynamic json) {
|
||||
message = json['message'];
|
||||
if (json['data'] != null) {
|
||||
data = [];
|
||||
json['data'].forEach((v) {
|
||||
data?.add(BranchData.fromJson(v));
|
||||
});
|
||||
}
|
||||
}
|
||||
String? message;
|
||||
List<BranchData>? data;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['message'] = message;
|
||||
if (data != null) {
|
||||
map['data'] = data?.map((v) => v.toJson()).toList();
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
class BranchData {
|
||||
BranchData({
|
||||
this.id,
|
||||
this.businessId,
|
||||
this.name,
|
||||
this.phone,
|
||||
this.email,
|
||||
this.address,
|
||||
this.description,
|
||||
this.status,
|
||||
this.isMain,
|
||||
this.branchOpeningBalance,
|
||||
this.branchRemainingBalance,
|
||||
this.deletedAt,
|
||||
this.createdAt,
|
||||
this.updatedAt,
|
||||
});
|
||||
|
||||
BranchData.fromJson(dynamic json) {
|
||||
id = json['id'];
|
||||
businessId = json['business_id'];
|
||||
name = json['name'];
|
||||
phone = json['phone'];
|
||||
email = json['email'];
|
||||
address = json['address'];
|
||||
description = json['description'];
|
||||
status = json['status'];
|
||||
isMain = json['is_main'];
|
||||
branchOpeningBalance = json['branchOpeningBalance'];
|
||||
branchRemainingBalance = json['branchRemainingBalance'];
|
||||
deletedAt = json['deleted_at'];
|
||||
createdAt = json['created_at'];
|
||||
updatedAt = json['updated_at'];
|
||||
}
|
||||
num? id;
|
||||
num? businessId;
|
||||
String? name;
|
||||
String? phone;
|
||||
String? email;
|
||||
String? address;
|
||||
String? description;
|
||||
num? status;
|
||||
num? isMain;
|
||||
num? branchOpeningBalance;
|
||||
num? branchRemainingBalance;
|
||||
String? deletedAt;
|
||||
String? createdAt;
|
||||
String? updatedAt;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['id'] = id;
|
||||
map['business_id'] = businessId;
|
||||
map['name'] = name;
|
||||
map['phone'] = phone;
|
||||
map['email'] = email;
|
||||
map['address'] = address;
|
||||
map['description'] = description;
|
||||
map['status'] = status;
|
||||
map['is_main'] = isMain;
|
||||
map['branchOpeningBalance'] = branchOpeningBalance;
|
||||
map['branchRemainingBalance'] = branchRemainingBalance;
|
||||
map['deleted_at'] = deletedAt;
|
||||
map['created_at'] = createdAt;
|
||||
map['updated_at'] = updatedAt;
|
||||
return map;
|
||||
}
|
||||
}
|
||||
5
lib/Screens/branch/provider/branch_list_provider.dart
Normal file
5
lib/Screens/branch/provider/branch_list_provider.dart
Normal file
@@ -0,0 +1,5 @@
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:mobile_pos/Screens/branch/model/branch_list_model.dart';
|
||||
import 'package:mobile_pos/Screens/branch/repo/branch_repo.dart';
|
||||
|
||||
final branchListProvider = FutureProvider.autoDispose<BranchListModel>((ref) => BranchRepo().fetchBranchList());
|
||||
228
lib/Screens/branch/repo/branch_repo.dart
Normal file
228
lib/Screens/branch/repo/branch_repo.dart
Normal file
@@ -0,0 +1,228 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:mobile_pos/Screens/branch/provider/branch_list_provider.dart';
|
||||
import '../../../Const/api_config.dart';
|
||||
import '../../../Repository/constant_functions.dart';
|
||||
import '../../../http_client/custome_http_client.dart';
|
||||
import '../../../http_client/customer_http_client_get.dart';
|
||||
import '../model/branch_list_model.dart';
|
||||
|
||||
class BranchRepo {
|
||||
Future<BranchListModel> fetchBranchList() async {
|
||||
CustomHttpClientGet clientGet = CustomHttpClientGet(client: http.Client());
|
||||
final uri = Uri.parse('${APIConfig.url}/branches');
|
||||
|
||||
final response = await clientGet.get(url: uri);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final parsedData = jsonDecode(response.body);
|
||||
|
||||
return BranchListModel.fromJson(parsedData);
|
||||
} else {
|
||||
throw Exception('Failed to fetch Branch List');
|
||||
}
|
||||
}
|
||||
Future<void> createBranch({
|
||||
required WidgetRef ref,
|
||||
required BuildContext context,
|
||||
required String name,
|
||||
required String phone,
|
||||
required String email,
|
||||
required String address,
|
||||
required String branchOpeningBalance,
|
||||
required String description,
|
||||
}) async {
|
||||
final uri = Uri.parse('${APIConfig.url}/branches');
|
||||
CustomHttpClient customHttpClient = CustomHttpClient(client: http.Client(), context: context, ref: ref);
|
||||
|
||||
final requestBody = json.encode({
|
||||
"name": name,
|
||||
"phone": phone,
|
||||
"email": email,
|
||||
"address": address,
|
||||
"branchOpeningBalance": branchOpeningBalance,
|
||||
"description": description,
|
||||
});
|
||||
|
||||
try {
|
||||
var responseData = await customHttpClient.post(
|
||||
url: uri,
|
||||
body: requestBody,
|
||||
addContentTypeInHeader: true,
|
||||
);
|
||||
|
||||
EasyLoading.dismiss();
|
||||
final parsedData = jsonDecode(responseData.body);
|
||||
|
||||
if (responseData.statusCode == 200) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('Branch created successfully!')),
|
||||
);
|
||||
ref.refresh(branchListProvider);
|
||||
Navigator.pop(context);
|
||||
} else {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('Failed: ${parsedData['message']}')),
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
EasyLoading.dismiss();
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('Error: $error')),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> updateBranch({
|
||||
required WidgetRef ref,
|
||||
required BuildContext context,
|
||||
required String id,
|
||||
required String name,
|
||||
required String phone,
|
||||
required String email,
|
||||
required String address,
|
||||
required String branchOpeningBalance,
|
||||
required String description,
|
||||
}) async {
|
||||
final uri = Uri.parse('${APIConfig.url}/branches/$id');
|
||||
CustomHttpClient customHttpClient =
|
||||
CustomHttpClient(client: http.Client(), context: context, ref: ref);
|
||||
|
||||
final body = {
|
||||
"name": name,
|
||||
"phone": phone,
|
||||
"email": email,
|
||||
"address": address,
|
||||
"branchOpeningBalance": branchOpeningBalance,
|
||||
"description": description,
|
||||
"_method": "put", // Laravel PUT simulation
|
||||
};
|
||||
|
||||
try {
|
||||
var responseData = await customHttpClient.post(
|
||||
url: uri,
|
||||
body: body,
|
||||
addContentTypeInHeader: false,
|
||||
);
|
||||
|
||||
EasyLoading.dismiss();
|
||||
final parsedData = jsonDecode(responseData.body);
|
||||
|
||||
if (responseData.statusCode == 200) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('Branch updated successfully!')),
|
||||
);
|
||||
ref.refresh(branchListProvider);
|
||||
Navigator.pop(context);
|
||||
} else {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('Failed: ${parsedData['message']}')),
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
EasyLoading.dismiss();
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('Error: $error')),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// switch Branch
|
||||
Future<bool> switchBranch({required String id}) async {
|
||||
EasyLoading.show(status: 'Processing');
|
||||
final url = Uri.parse('${APIConfig.url}/switch-branch/$id');
|
||||
final headers = {
|
||||
'Accept': 'application/json',
|
||||
'Authorization': await getAuthToken(),
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
try {
|
||||
var response = await http.get(
|
||||
url,
|
||||
headers: headers,
|
||||
);
|
||||
EasyLoading.dismiss();
|
||||
print(response.statusCode);
|
||||
if (response.statusCode == 200) {
|
||||
return true;
|
||||
} else {
|
||||
var data = jsonDecode(response.body);
|
||||
EasyLoading.showError(data['message'] ?? 'Failed to switch');
|
||||
print(data['message']);
|
||||
return false;
|
||||
}
|
||||
} catch (e) {
|
||||
EasyLoading.dismiss();
|
||||
EasyLoading.showError('Error: ${e.toString()}');
|
||||
print(e.toString());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> deleteUser({
|
||||
required String id,
|
||||
required BuildContext context,
|
||||
required WidgetRef ref,
|
||||
}) async {
|
||||
EasyLoading.show(status: 'Processing');
|
||||
final url = Uri.parse('${APIConfig.url}/branches/$id');
|
||||
try {
|
||||
CustomHttpClient customHttpClient = CustomHttpClient(ref: ref, context: context, client: http.Client());
|
||||
var response = await customHttpClient.delete(
|
||||
url: url,
|
||||
);
|
||||
EasyLoading.dismiss();
|
||||
print(response.statusCode);
|
||||
if (response.statusCode == 200) {
|
||||
return true;
|
||||
} else {
|
||||
var data = jsonDecode(response.body);
|
||||
EasyLoading.showError(data['message'] ?? 'Failed to delete');
|
||||
print(data['message']);
|
||||
return false;
|
||||
}
|
||||
} catch (e) {
|
||||
EasyLoading.dismiss();
|
||||
EasyLoading.showError('Error: ${e.toString()}');
|
||||
print(e.toString());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// switch Branch
|
||||
Future<bool> exitBranch({required String id}) async {
|
||||
EasyLoading.show(status: 'Processing');
|
||||
final url = Uri.parse('${APIConfig.url}/exit-branch/$id');
|
||||
final headers = {
|
||||
'Accept': 'application/json',
|
||||
'Authorization': await getAuthToken(),
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
try {
|
||||
var response = await http.get(
|
||||
url,
|
||||
headers: headers,
|
||||
);
|
||||
EasyLoading.dismiss();
|
||||
print(response.statusCode);
|
||||
if (response.statusCode == 200) {
|
||||
return true;
|
||||
} else {
|
||||
var data = jsonDecode(response.body);
|
||||
EasyLoading.showError(data['message'] ?? 'Failed to exit');
|
||||
print(data['message']);
|
||||
return false;
|
||||
}
|
||||
} catch (e) {
|
||||
EasyLoading.dismiss();
|
||||
EasyLoading.showError('Error: ${e.toString()}');
|
||||
print(e.toString());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user