feat: Add contact picking functionality in employee form and update dependencies #45
@ -1,6 +1,10 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<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
|
||||
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:logger/logger.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 {
|
||||
male,
|
||||
@ -18,7 +20,7 @@ final Logger logger = Logger();
|
||||
|
||||
class AddEmployeeController extends MyController {
|
||||
List<PlatformFile> files = [];
|
||||
MyFormValidator basicValidator = MyFormValidator();
|
||||
final MyFormValidator basicValidator = MyFormValidator();
|
||||
Gender? selectedGender;
|
||||
List<Map<String, dynamic>> roles = [];
|
||||
String? selectedRoleId;
|
||||
@ -59,11 +61,18 @@ class AddEmployeeController extends MyController {
|
||||
};
|
||||
String selectedCountryCode = "+91";
|
||||
|
||||
bool showOnline = true;
|
||||
final List<String> categories = [];
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
logger.i("Initializing AddEmployeeController...");
|
||||
_initializeFields();
|
||||
fetchRoles();
|
||||
}
|
||||
|
||||
void _initializeFields() {
|
||||
basicValidator.addField(
|
||||
'first_name',
|
||||
label: "First Name",
|
||||
@ -85,10 +94,6 @@ class AddEmployeeController extends MyController {
|
||||
logger.i("Fields initialized for first_name, phone_number, last_name.");
|
||||
}
|
||||
|
||||
bool showOnline = true;
|
||||
|
||||
final List<String> categories = [];
|
||||
|
||||
void onGenderSelected(Gender? gender) {
|
||||
selectedGender = gender;
|
||||
logger.i("Gender selected: ${gender?.name}");
|
||||
@ -97,13 +102,17 @@ class AddEmployeeController extends MyController {
|
||||
|
||||
Future<void> fetchRoles() async {
|
||||
logger.i("Fetching roles...");
|
||||
final result = await ApiService.getRoles();
|
||||
if (result != null) {
|
||||
roles = List<Map<String, dynamic>>.from(result);
|
||||
logger.i("Roles fetched successfully.");
|
||||
update();
|
||||
} else {
|
||||
logger.e("Failed to fetch roles.");
|
||||
try {
|
||||
final result = await ApiService.getRoles();
|
||||
if (result != null) {
|
||||
roles = List<Map<String, dynamic>>.from(result);
|
||||
logger.i("Roles fetched successfully.");
|
||||
update();
|
||||
} 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(
|
||||
"Creating employee with Name: $firstName $lastName, Phone: $phoneNumber, Gender: ${selectedGender!.name}");
|
||||
|
||||
final response = await ApiService.createEmployee(
|
||||
firstName: firstName!,
|
||||
lastName: lastName!,
|
||||
phoneNumber: phoneNumber!,
|
||||
gender: selectedGender!.name,
|
||||
jobRoleId: selectedRoleId!,
|
||||
);
|
||||
|
||||
if (response == true) {
|
||||
logger.i("Employee created successfully.");
|
||||
showAppSnackbar(
|
||||
title: "Success",
|
||||
message: "Employee created successfully!",
|
||||
type: SnackbarType.success,
|
||||
try {
|
||||
final response = await ApiService.createEmployee(
|
||||
firstName: firstName!,
|
||||
lastName: lastName!,
|
||||
phoneNumber: phoneNumber!,
|
||||
gender: selectedGender!.name,
|
||||
jobRoleId: selectedRoleId!,
|
||||
);
|
||||
|
||||
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;
|
||||
} 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(
|
||||
title: "Error",
|
||||
message: "Failed to create employee.",
|
||||
message: "Failed to fetch contacts.",
|
||||
type: SnackbarType.error,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -214,7 +214,17 @@ class _AddEmployeeBottomSheetState extends State<AddEmployeeBottomSheet>
|
||||
},
|
||||
keyboardType: TextInputType.phone,
|
||||
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: ^1.9.0
|
||||
percent_indicator: ^4.2.2
|
||||
flutter_contacts: ^1.1.9+2
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
Loading…
x
Reference in New Issue
Block a user