first commit

This commit is contained in:
2026-02-07 15:57:09 +07:00
commit 157096f164
1153 changed files with 415766 additions and 0 deletions

View File

@@ -0,0 +1,127 @@
import 'package:flutter/material.dart';
import 'package:mobile_pos/constant.dart';
import 'package:mobile_pos/generated/l10n.dart' as lang;
class ContactUs extends StatefulWidget {
const ContactUs({Key? key}) : super(key: key);
@override
// ignore: library_private_types_in_public_api
_ContactUsState createState() => _ContactUsState();
}
class _ContactUsState extends State<ContactUs> {
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Scaffold(
appBar: AppBar(
title: Text(
lang.S.of(context).contactUs,
),
iconTheme: const IconThemeData(color: Colors.black),
centerTitle: true,
backgroundColor: Colors.white,
elevation: 0.0,
),
body: Column(
children: [
Center(
// ignore: sized_box_for_whitespace
child: Container(
height: 150.0,
width: MediaQuery.of(context).size.width - 40,
child: TextField(
keyboardType: TextInputType.name,
maxLines: 30,
decoration: InputDecoration(
border: const OutlineInputBorder(),
hintText: lang.S.of(context).writeYourMessageHere,
),
),
),
),
Padding(
padding: const EdgeInsets.all(10.0),
child: ElevatedButton(
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) => Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.0),
),
// ignore: sized_box_for_whitespace
child: Container(
height: 350.0,
width: MediaQuery.of(context).size.width - 80,
child: Column(
children: [
Row(
children: [
const Spacer(),
IconButton(
color: kGreyTextColor,
icon: const Icon(Icons.cancel_outlined),
onPressed: () {
Navigator.pop(context);
},
),
],
),
Container(
height: 100.0,
width: 100.0,
decoration: BoxDecoration(
color: kDarkWhite,
borderRadius: BorderRadius.circular(10.0),
),
child: const Center(
child: Image(
image: AssetImage('images/emailsent.png'),
),
),
),
const SizedBox(
height: 20.0,
),
Center(
child: Text(
lang.S.of(context).sendYourEmail,
style: theme.textTheme.titleLarge,
),
),
Center(
child: Padding(
padding: EdgeInsets.all(8.0),
child: Text(
lang.S.of(context).loremIpsumDolorSitAmetConsecteturElitInterdumCons,
//'Lorem ipsum dolor sit amet, consectetur elit. Interdum cons.',
maxLines: 2,
textAlign: TextAlign.center,
style: theme.textTheme.bodyLarge?.copyWith(
color: kGreyTextColor,
),
),
),
),
ElevatedButton(
onPressed: () {
Navigator.pop(context);
},
child: Text(lang.S.of(context).backToHome),
),
],
),
),
),
);
},
child: Text(lang.S.of(context).sendMessage),
),
),
],
),
);
}
}

View File

@@ -0,0 +1,44 @@
import 'dart:convert';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:http/http.dart' as http;
import 'package:mobile_pos/Const/api_config.dart';
import '../../../../Repository/constant_functions.dart';
class DeleteAccountRepository {
Future<bool> deleteAccount({
required String businessId,
required String password,
}) async {
EasyLoading.show();
final url = Uri.parse('${APIConfig.url}/business-delete');
try {
var request = http.MultipartRequest('POST', url);
// Add headers
request.headers.addAll({
'Accept': 'application/json',
'Authorization': await getAuthToken(),
});
request.fields['password'] = password;
// Send request
final streamedResponse = await request.send();
final response = await http.Response.fromStream(streamedResponse);
if (response.statusCode == 200) {
await EasyLoading.showSuccess('Account deleted successfully', duration: Duration(seconds: 2));
return true;
} else {
final result = jsonDecode(response.body);
EasyLoading.showError(result['message']);
return false;
}
} catch (e) {
print('Delete exception: $e');
EasyLoading.showError('Something went wrong');
return false;
}
}
}

View File

@@ -0,0 +1,103 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:mobile_pos/Provider/profile_provider.dart';
import 'package:mobile_pos/constant.dart';
import 'package:mobile_pos/generated/l10n.dart' as lang;
import '../Authentication/Repo/logout_repo.dart';
import 'account detele/repo/delete_account_repo.dart';
void showDeleteAccountDialog(BuildContext context, WidgetRef ref) {
final _lang = lang.S.of(context);
final TextEditingController passwordController = TextEditingController();
final GlobalKey<FormState> formKey = GlobalKey<FormState>();
bool isChecked = false;
showDialog(
context: context,
builder: (context) {
return StatefulBuilder(
builder: (context, setState) {
// AlertDialog-er baire theke SingleChildScrollView soriye deya hoyeche
return AlertDialog(
title: Text(_lang.deleteAcc),
content: SingleChildScrollView(
// Ekhane use korun
child: Form(
key: formKey,
child: Column(
mainAxisSize: MainAxisSize.min, // Column-ke tar dorkari jayga nite bolbe
children: [
Text(
_lang.deleteDialogDetails,
style: TextStyle(color: kMainColor),
),
const SizedBox(height: 16),
TextFormField(
controller: passwordController,
obscureText: true,
decoration: InputDecoration(
labelText: _lang.enterYourPassword,
border: const OutlineInputBorder(),
),
validator: (value) {
if (value == null || value.isEmpty) {
return _lang.passwordIsRequired;
}
if (value.length < 6) {
return _lang.passwordMust6Character;
}
return null;
},
),
const SizedBox(height: 16),
CheckboxListTile(
value: isChecked,
onChanged: (value) {
setState(() {
isChecked = value ?? false;
});
},
contentPadding: EdgeInsets.zero,
title: Text(_lang.iAgreeDeleteMyAccountPermanent),
controlAffinity: ListTileControlAffinity.leading,
),
],
),
),
),
actions: [
TextButton(
child: Text(_lang.cancel),
onPressed: () => Navigator.of(context).pop(),
),
ElevatedButton(
onPressed: isChecked
? () async {
if (formKey.currentState!.validate()) {
// ref.read use kora better callback function-er bhetor
final businessId = ref.read(businessInfoProvider).value?.data?.id.toString() ?? '';
final bool isDeleted = await DeleteAccountRepository()
.deleteAccount(businessId: businessId, password: passwordController.text);
if (isDeleted) {
await LogOutRepo().signOut();
if (context.mounted) Navigator.of(context).pop();
}
}
}
: null,
style: ElevatedButton.styleFrom(
backgroundColor: kMainColor,
foregroundColor: Colors.white,
),
child: Text(_lang.delete),
),
],
);
},
);
},
);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,29 @@
import 'dart:convert';
import 'package:flutter/cupertino.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:mobile_pos/http_client/custome_http_client.dart';
import 'package:http/http.dart' as http;
import '../../../../Const/api_config.dart';
class InvoiceSizeRepo {
Future<bool> invoiceSizeChange({required String? invoiceSize, required WidgetRef ref, required BuildContext context}) async {
EasyLoading.show();
CustomHttpClient client = CustomHttpClient(client: http.Client(), ref: ref, context: context);
final url = Uri.parse('${APIConfig.url}/invoice-settings/update');
try {
final response = await client.post(url: url, body: {'invoice_size': invoiceSize});
final massage = json.decode(response.body)['message'];
if (response.statusCode == 200) {
EasyLoading.showSuccess(massage);
return true;
}
EasyLoading.showError(massage);
return false;
} catch (e) {
EasyLoading.showError(e.toString());
return false;
}
}
}

View File

@@ -0,0 +1,42 @@
import 'package:mobile_pos/generated/l10n.dart' as lang;
class AmountRoundingDropdownModel {
late String value;
late String option;
AmountRoundingDropdownModel({required this.value, required this.option});
}
final List<AmountRoundingDropdownModel> roundingMethods = [
AmountRoundingDropdownModel(value: 'none', option: lang.S.current.none),
AmountRoundingDropdownModel(value: 'round_up', option: lang.S.current.roundToWholeNumber),
AmountRoundingDropdownModel(value: 'nearest_whole_number', option: lang.S.current.roundToNearestWholeNumber),
AmountRoundingDropdownModel(value: 'nearest_0.05', option: lang.S.current.roundToNearnessDecimalNumber005),
AmountRoundingDropdownModel(value: 'nearest_0.1', option: lang.S.current.roundToNearnessDecimalNumber01),
AmountRoundingDropdownModel(value: 'nearest_0.5', option: lang.S.current.roundToNearnessDecimalNumber05),
];
num roundNumber({required num value, required String roundingType}) {
switch (roundingType) {
case "none":
return value;
case "round_up":
return value.ceilToDouble();
case "nearest_whole_number":
return value.roundToDouble();
case "nearest_0.05":
return (value / 0.05).round() * 0.05;
case "nearest_0.1":
return (value / 0.1).round() * 0.1;
case "nearest_0.5":
return (value / 0.5).round() * 0.5;
default:
return value;
}
}

View File

@@ -0,0 +1,116 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:mobile_pos/generated/l10n.dart' as lang;
import '../../../GlobalComponents/glonal_popup.dart';
import '../../../Provider/profile_provider.dart';
import '../../../Repository/API/business_info_update_repo.dart';
import '../../../constant.dart';
import 'model/amount_rounding_dropdown_model.dart';
class SalesSettingsScreen extends ConsumerStatefulWidget {
const SalesSettingsScreen({super.key});
@override
ConsumerState<SalesSettingsScreen> createState() => _PrintingInvoiceScreenState();
}
class _PrintingInvoiceScreenState extends ConsumerState<SalesSettingsScreen> {
@override
void initState() {
// TODO: implement initState
super.initState();
ref.read(businessInfoProvider).when(
data: (data) {
setState(() {
selectedMethod = roundingMethods.firstWhere(
(element) => element.value == data.data?.saleRoundingOption,
);
});
},
error: (error, stackTrace) {},
loading: () {},
);
}
AmountRoundingDropdownModel? selectedMethod = roundingMethods[0];
@override
Widget build(BuildContext context) {
final _lang = lang.S.of(context);
return GlobalPopup(
child: Scaffold(
backgroundColor: kWhite,
appBar: AppBar(
iconTheme: const IconThemeData(color: Colors.black),
title: Text(
_lang.salesSetting,
),
centerTitle: true,
backgroundColor: Colors.white,
elevation: 0.0,
),
bottomNavigationBar: Padding(
padding: const EdgeInsets.all(10.0),
child: ElevatedButton(
child: Text(lang.S.of(context).save),
onPressed: () async {
ref.watch(businessInfoProvider).when(
data: (data) async {
final businessRepository = BusinessUpdateRepository();
final isProfileUpdated = await businessRepository.updateSalesSettings(
id: data.data?.id.toString() ?? '',
ref: ref,
context: context,
saleRoundingOption: selectedMethod?.value,
);
if (isProfileUpdated) {
ref.refresh(businessInfoProvider);
Navigator.pop(context);
}
},
error: (error, stackTrace) {},
loading: () {},
);
},
),
),
body: Padding(
padding: const EdgeInsets.all(15.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 15,
children: [
Text(
'${_lang.amountRoundingMethod}:',
style: TextStyle(
fontSize: 16,
),
),
DropdownButtonFormField<AmountRoundingDropdownModel>(
isExpanded: true,
decoration: InputDecoration(
labelText: _lang.amountRoundingMethod,
border: OutlineInputBorder(),
),
value: selectedMethod,
items: roundingMethods.map((method) {
return DropdownMenuItem<AmountRoundingDropdownModel>(
value: method,
child: Text(method.option),
);
}).toList(),
onChanged: (value) {
setState(() {
selectedMethod = value;
});
},
),
],
),
),
),
);
}
}

View File

@@ -0,0 +1,278 @@
import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:mobile_pos/Const/api_config.dart';
import 'package:mobile_pos/Screens/DashBoard/dashboard.dart';
import 'package:mobile_pos/Screens/Profile%20Screen/profile_details.dart';
import 'package:mobile_pos/Screens/Settings/printing_invoice/printing_invoice_screen.dart';
import 'package:mobile_pos/Screens/Settings/sales%20settings/sales_settings_screen.dart';
import 'package:mobile_pos/Screens/User%20Roles/user_role_screen.dart';
import 'package:mobile_pos/Screens/cash%20and%20bank/bank%20account/bank_account_list_screen.dart';
import 'package:mobile_pos/Screens/cash%20and%20bank/cansh%20in%20hand/cash_in_hand_screen.dart';
import 'package:mobile_pos/generated/l10n.dart' as lang;
import 'package:nb_utils/nb_utils.dart';
import '../../GlobalComponents/glonal_popup.dart';
import '../../Provider/profile_provider.dart';
import '../../constant.dart';
import '../../currency.dart';
import '../../widgets/page_navigation_list/_page_navigation_list.dart';
import '../Authentication/Repo/logout_repo.dart';
import '../Currency/currency_screen.dart';
import '../../service/check_user_role_permission_provider.dart';
import '../barcode/gererate_barcode.dart';
import '../cash and bank/cheques/cheques_list_screen.dart';
import '../language/language.dart';
import '../subscription/package_screen.dart';
import 'delete_acount_allart_dialog.dart';
class SettingScreen extends ConsumerStatefulWidget {
const SettingScreen({super.key});
@override
ConsumerState<SettingScreen> createState() => SettingScreenState();
}
class SettingScreenState extends ConsumerState<SettingScreen> {
bool expanded = false;
bool expandedHelp = false;
bool expandedAbout = false;
bool selected = false;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
printerIsEnable();
});
}
void printerIsEnable() async {
final prefs = await SharedPreferences.getInstance();
setState(() => isPrintEnable = prefs.getBool('isPrintEnable') ?? true);
}
@override
Widget build(BuildContext context) {
final _theme = Theme.of(context);
return SafeArea(
child: Consumer(
builder: (context, ref, _) {
final businessInfo = ref.watch(businessInfoProvider);
return GlobalPopup(
child: Scaffold(
backgroundColor: kWhite,
appBar: PreferredSize(
preferredSize: const Size.fromHeight(80),
child: Padding(
padding: const EdgeInsets.only(top: 8),
child: Builder(
builder: (_) {
final _details = businessInfo.value;
return ListTile(
leading: GestureDetector(
onTap: () => const ProfileDetails().launch(context),
child: Container(
constraints: BoxConstraints.tight(
const Size.square(54),
),
decoration: BoxDecoration(
image: _details?.data?.pictureUrl == null
? const DecorationImage(
image: AssetImage('images/no_shop_image.png'),
fit: BoxFit.cover,
)
: DecorationImage(
image: NetworkImage(
APIConfig.domain + (_details?.data?.pictureUrl ?? ''),
),
fit: BoxFit.cover,
),
borderRadius: BorderRadius.circular(50),
),
),
),
title: Text(
_details?.data?.user?.role == 'staff'
? '${_details?.data?.companyName ?? ''} [${_details?.data?.user?.name ?? ''}]'
: _details?.data?.companyName ?? '',
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
titleTextStyle: _theme.textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.w600,
),
subtitle: Text(
_details?.data?.category?.name ?? '',
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
subtitleTextStyle: _theme.textTheme.bodyLarge?.copyWith(
color: kGreyTextColor,
),
);
},
),
),
),
body: PageNavigationListView(
navTiles: navItems,
onTap: (value) async {
if (value.type == PageNavigationListTileType.navigation && value.route != null) {
if (value.route is SelectLanguage) {
final prefs = await SharedPreferences.getInstance();
final data = prefs.getString('lang');
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => SelectLanguage(
alreadySelectedLanguage: data,
),
),
);
return;
}
final _previousCurrency = currency;
await Navigator.of(context).push(MaterialPageRoute(builder: (_) => value.route!)).then(
(_) => (_previousCurrency != currency) ? setState(() {}) : null,
);
}
if (value.type == PageNavigationListTileType.function) {
if (value.value == 'logout') {
ref.invalidate(businessInfoProvider);
EasyLoading.show(status: lang.S.of(context).logOut);
LogOutRepo repo = LogOutRepo();
await repo.signOutApi();
}
if (value.value == 'delete_account') {
showDeleteAccountDialog(context, ref);
}
}
},
// footer: Padding(
// padding: const EdgeInsetsDirectional.only(
// start: 24,
// top: 8,
// ),
// child: Text(
// 'POSPro V-$appVersion',
// style: _theme.textTheme.bodyLarge?.copyWith(
// color: kGreyTextColor,
// ),
// ),
// ),
),
),
);
},
),
);
}
List<PageNavigationNavTile<dynamic>> get navItems {
return [
PageNavigationNavTile(
title: lang.S.of(context).profile,
svgIconPath: 'assets/profile.svg',
route: const ProfileDetails(),
),
PageNavigationNavTile(
title: lang.S.of(context).printingInvoice,
svgIconPath: 'assets/print.svg',
route: PrintingInvoiceScreen(),
),
PageNavigationNavTile(
title: lang.S.of(context).salesSetting,
svgIconPath: 'assets/sales.svg',
route: SalesSettingsScreen(),
),
PageNavigationNavTile(
title: lang.S.of(context).subscription,
svgIconPath: 'assets/subscription.svg',
route: const PackageScreen(),
),
PageNavigationNavTile(
title: lang.S.of(context).dashboard,
svgIconPath: 'assets/dashboard.svg',
route: const DashboardScreen(),
),
if (PermissionService(ref).hasPermission(Permit.rolesRead.value))
PageNavigationNavTile(
title: lang.S.of(context).userRole,
svgIconPath: 'assets/userRole.svg',
route: const UserRoleScreen(),
),
/// NEW EXPANSION TILE: CASH & BANK
PageNavigationNavTile(
title: lang.S.of(context).cashAndBank,
svgIconPath: 'assets/cash_bank.svg',
type: PageNavigationListTileType.expansion,
children: [
PageNavigationNavTile(
title: lang.S.of(context).bankAccounts,
svgIconPath: 'assets/bank.svg',
route: BankAccountListScreen(),
),
PageNavigationNavTile(
title: lang.S.of(context).cashInHand,
svgIconPath: 'assets/cash.svg',
route: CashInHandScreen(),
),
PageNavigationNavTile(
title: lang.S.of(context).cheque,
svgIconPath: 'assets/cheque.svg',
route: ChequesListScreen(),
),
],
),
PageNavigationNavTile(
title: lang.S.of(context).currency,
svgIconPath: 'assets/currency.svg',
route: const CurrencyScreen(),
trailing: Text.rich(
TextSpan(
text: '($currency) ',
children: const [
WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: Icon(
Icons.arrow_forward_ios,
size: 20,
color: kGreyTextColor,
),
),
],
),
style: Theme.of(context).textTheme.bodyLarge,
),
),
PageNavigationNavTile(
title: lang.S.of(context).barcodeGenerator,
svgIconPath: 'assets/barcode.svg',
route: const BarcodeGeneratorScreen(),
),
PageNavigationNavTile(
title: lang.S.of(context).selectLang,
svgIconPath: 'assets/language.svg',
route: const SelectLanguage(),
),
PageNavigationNavTile(
title: lang.S.of(context).deleteAcc,
svgIconPath: 'assets/account_delete.svg',
value: 'delete_account',
hideTrailing: true,
type: PageNavigationListTileType.function,
),
PageNavigationNavTile(
title: lang.S.of(context).logOut,
svgIconPath: 'assets/logout.svg',
value: 'logout',
hideTrailing: true,
type: PageNavigationListTileType.function,
),
];
}
}