Merge pull request 'feat: Add contact picking functionality in employee form and update dependencies' (#45) from Vaibhav_Enhancement-#454 into main
Reviewed-on: #45
This commit is contained in:
commit
3926e762e5
@ -1,6 +1,10 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<uses-permission android:name="android.permission.CAMERA" />
|
<uses-permission android:name="android.permission.CAMERA" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||||
|
<uses-permission android:name="android.permission.READ_CONTACTS"/>
|
||||||
|
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:label="marco"
|
android:label="marco"
|
||||||
|
@ -5,6 +5,8 @@ import 'package:marco/helpers/widgets/my_form_validator.dart';
|
|||||||
import 'package:marco/helpers/services/api_service.dart';
|
import 'package:marco/helpers/services/api_service.dart';
|
||||||
import 'package:logger/logger.dart';
|
import 'package:logger/logger.dart';
|
||||||
import 'package:marco/helpers/widgets/my_snackbar.dart';
|
import 'package:marco/helpers/widgets/my_snackbar.dart';
|
||||||
|
import 'package:flutter_contacts/flutter_contacts.dart';
|
||||||
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
|
|
||||||
enum Gender {
|
enum Gender {
|
||||||
male,
|
male,
|
||||||
@ -18,7 +20,7 @@ final Logger logger = Logger();
|
|||||||
|
|
||||||
class AddEmployeeController extends MyController {
|
class AddEmployeeController extends MyController {
|
||||||
List<PlatformFile> files = [];
|
List<PlatformFile> files = [];
|
||||||
MyFormValidator basicValidator = MyFormValidator();
|
final MyFormValidator basicValidator = MyFormValidator();
|
||||||
Gender? selectedGender;
|
Gender? selectedGender;
|
||||||
List<Map<String, dynamic>> roles = [];
|
List<Map<String, dynamic>> roles = [];
|
||||||
String? selectedRoleId;
|
String? selectedRoleId;
|
||||||
@ -59,11 +61,18 @@ class AddEmployeeController extends MyController {
|
|||||||
};
|
};
|
||||||
String selectedCountryCode = "+91";
|
String selectedCountryCode = "+91";
|
||||||
|
|
||||||
|
bool showOnline = true;
|
||||||
|
final List<String> categories = [];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
super.onInit();
|
super.onInit();
|
||||||
logger.i("Initializing AddEmployeeController...");
|
logger.i("Initializing AddEmployeeController...");
|
||||||
|
_initializeFields();
|
||||||
fetchRoles();
|
fetchRoles();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _initializeFields() {
|
||||||
basicValidator.addField(
|
basicValidator.addField(
|
||||||
'first_name',
|
'first_name',
|
||||||
label: "First Name",
|
label: "First Name",
|
||||||
@ -85,10 +94,6 @@ class AddEmployeeController extends MyController {
|
|||||||
logger.i("Fields initialized for first_name, phone_number, last_name.");
|
logger.i("Fields initialized for first_name, phone_number, last_name.");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool showOnline = true;
|
|
||||||
|
|
||||||
final List<String> categories = [];
|
|
||||||
|
|
||||||
void onGenderSelected(Gender? gender) {
|
void onGenderSelected(Gender? gender) {
|
||||||
selectedGender = gender;
|
selectedGender = gender;
|
||||||
logger.i("Gender selected: ${gender?.name}");
|
logger.i("Gender selected: ${gender?.name}");
|
||||||
@ -97,13 +102,17 @@ class AddEmployeeController extends MyController {
|
|||||||
|
|
||||||
Future<void> fetchRoles() async {
|
Future<void> fetchRoles() async {
|
||||||
logger.i("Fetching roles...");
|
logger.i("Fetching roles...");
|
||||||
final result = await ApiService.getRoles();
|
try {
|
||||||
if (result != null) {
|
final result = await ApiService.getRoles();
|
||||||
roles = List<Map<String, dynamic>>.from(result);
|
if (result != null) {
|
||||||
logger.i("Roles fetched successfully.");
|
roles = List<Map<String, dynamic>>.from(result);
|
||||||
update();
|
logger.i("Roles fetched successfully.");
|
||||||
} else {
|
update();
|
||||||
logger.e("Failed to fetch roles.");
|
} else {
|
||||||
|
logger.e("Failed to fetch roles: null result");
|
||||||
|
}
|
||||||
|
} catch (e, st) {
|
||||||
|
logger.e("Error fetching roles: $e", error: e, stackTrace: st);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,30 +142,149 @@ class AddEmployeeController extends MyController {
|
|||||||
logger.i(
|
logger.i(
|
||||||
"Creating employee with Name: $firstName $lastName, Phone: $phoneNumber, Gender: ${selectedGender!.name}");
|
"Creating employee with Name: $firstName $lastName, Phone: $phoneNumber, Gender: ${selectedGender!.name}");
|
||||||
|
|
||||||
final response = await ApiService.createEmployee(
|
try {
|
||||||
firstName: firstName!,
|
final response = await ApiService.createEmployee(
|
||||||
lastName: lastName!,
|
firstName: firstName!,
|
||||||
phoneNumber: phoneNumber!,
|
lastName: lastName!,
|
||||||
gender: selectedGender!.name,
|
phoneNumber: phoneNumber!,
|
||||||
jobRoleId: selectedRoleId!,
|
gender: selectedGender!.name,
|
||||||
);
|
jobRoleId: selectedRoleId!,
|
||||||
|
|
||||||
if (response == true) {
|
|
||||||
logger.i("Employee created successfully.");
|
|
||||||
showAppSnackbar(
|
|
||||||
title: "Success",
|
|
||||||
message: "Employee created successfully!",
|
|
||||||
type: SnackbarType.success,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (response == true) {
|
||||||
|
logger.i("Employee created successfully.");
|
||||||
|
showAppSnackbar(
|
||||||
|
title: "Success",
|
||||||
|
message: "Employee created successfully!",
|
||||||
|
type: SnackbarType.success,
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
logger.e("Failed to create employee (response false).");
|
||||||
|
}
|
||||||
|
} catch (e, st) {
|
||||||
|
logger.e("Error creating employee: $e", error: e, stackTrace: st);
|
||||||
|
}
|
||||||
|
|
||||||
|
showAppSnackbar(
|
||||||
|
title: "Error",
|
||||||
|
message: "Failed to create employee.",
|
||||||
|
type: SnackbarType.error,
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> _checkAndRequestContactsPermission() async {
|
||||||
|
final status = await Permission.contacts.request();
|
||||||
|
|
||||||
|
if (status.isGranted) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
}
|
||||||
logger.e("Failed to create employee.");
|
|
||||||
|
if (status.isPermanentlyDenied) {
|
||||||
|
await openAppSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
showAppSnackbar(
|
||||||
|
title: "Permission Required",
|
||||||
|
message:
|
||||||
|
"Please allow Contacts permission from settings to pick a contact.",
|
||||||
|
type: SnackbarType.warning,
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> pickContact(BuildContext context) async {
|
||||||
|
final permissionGranted = await _checkAndRequestContactsPermission();
|
||||||
|
|
||||||
|
if (!permissionGranted) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
final picked = await FlutterContacts.openExternalPick();
|
||||||
|
|
||||||
|
if (picked == null) return; // User canceled contact picking
|
||||||
|
|
||||||
|
final contact =
|
||||||
|
await FlutterContacts.getContact(picked.id, withProperties: true);
|
||||||
|
|
||||||
|
if (contact == null) {
|
||||||
|
showAppSnackbar(
|
||||||
|
title: "Error",
|
||||||
|
message: "Failed to load contact details.",
|
||||||
|
type: SnackbarType.error,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (contact.phones.isEmpty) {
|
||||||
|
showAppSnackbar(
|
||||||
|
title: "No Phone Number",
|
||||||
|
message: "Selected contact has no phone number.",
|
||||||
|
type: SnackbarType.warning,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final indiaPhones = contact.phones.where((p) {
|
||||||
|
final normalized = p.number.replaceAll(RegExp(r'[^0-9+]'), '');
|
||||||
|
return normalized.startsWith('+91') ||
|
||||||
|
RegExp(r'^\d{10}$').hasMatch(normalized);
|
||||||
|
}).toList();
|
||||||
|
|
||||||
|
if (indiaPhones.isEmpty) {
|
||||||
|
showAppSnackbar(
|
||||||
|
title: "No Indian Number",
|
||||||
|
message: "Selected contact has no Indian (+91) phone number.",
|
||||||
|
type: SnackbarType.warning,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String? selectedPhone;
|
||||||
|
|
||||||
|
if (indiaPhones.length == 1) {
|
||||||
|
selectedPhone = indiaPhones.first.number;
|
||||||
|
} else {
|
||||||
|
selectedPhone = await showDialog<String>(
|
||||||
|
context: context,
|
||||||
|
builder: (ctx) => AlertDialog(
|
||||||
|
title: Text("Choose an Indian number"),
|
||||||
|
content: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: indiaPhones
|
||||||
|
.map((p) => ListTile(
|
||||||
|
title: Text(p.number),
|
||||||
|
onTap: () => Navigator.of(ctx).pop(p.number),
|
||||||
|
))
|
||||||
|
.toList(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (selectedPhone == null) return;
|
||||||
|
}
|
||||||
|
final normalizedPhone = selectedPhone.replaceAll(RegExp(r'[^0-9]'), '');
|
||||||
|
|
||||||
|
// Remove country code prefix if present, keep only 10 digits
|
||||||
|
String phoneWithoutCountryCode;
|
||||||
|
|
||||||
|
if (normalizedPhone.length > 10) {
|
||||||
|
phoneWithoutCountryCode =
|
||||||
|
normalizedPhone.substring(normalizedPhone.length - 10);
|
||||||
|
} else {
|
||||||
|
phoneWithoutCountryCode = normalizedPhone;
|
||||||
|
}
|
||||||
|
|
||||||
|
basicValidator.getController('phone_number')?.text =
|
||||||
|
phoneWithoutCountryCode;
|
||||||
|
|
||||||
|
update();
|
||||||
|
} catch (e, st) {
|
||||||
|
logger.e("Error fetching contacts: $e", error: e, stackTrace: st);
|
||||||
showAppSnackbar(
|
showAppSnackbar(
|
||||||
title: "Error",
|
title: "Error",
|
||||||
message: "Failed to create employee.",
|
message: "Failed to fetch contacts.",
|
||||||
type: SnackbarType.error,
|
type: SnackbarType.error,
|
||||||
);
|
);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -214,7 +214,17 @@ class _AddEmployeeBottomSheetState extends State<AddEmployeeBottomSheet>
|
|||||||
},
|
},
|
||||||
keyboardType: TextInputType.phone,
|
keyboardType: TextInputType.phone,
|
||||||
decoration:
|
decoration:
|
||||||
_inputDecoration("eg: 9876543210"),
|
_inputDecoration("eg: 9876543210")
|
||||||
|
.copyWith(
|
||||||
|
suffixIcon: IconButton(
|
||||||
|
icon: Icon(Icons.contacts),
|
||||||
|
tooltip: "Pick from contacts",
|
||||||
|
onPressed: () async {
|
||||||
|
await controller
|
||||||
|
.pickContact(context);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -68,6 +68,7 @@ dependencies:
|
|||||||
path_provider: ^2.1.2
|
path_provider: ^2.1.2
|
||||||
path: ^1.9.0
|
path: ^1.9.0
|
||||||
percent_indicator: ^4.2.2
|
percent_indicator: ^4.2.2
|
||||||
|
flutter_contacts: ^1.1.9+2
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
Loading…
x
Reference in New Issue
Block a user