Commit 77774bea authored by Sai Srinivas's avatar Sai Srinivas
Browse files

Few Correction and fixes

parents 2a8fa440 3ac9e198
......@@ -12,6 +12,7 @@ import '../../Notifiers/HomeScreenNotifier.dart';
import '../../Notifiers/hrmProvider/attendanceListProvider.dart';
import '../../Utils/app_colors.dart';
import '../../Utils/commonWidgets.dart';
import '../../Utils/custom_snackbar.dart';
import '../CommonFilter2.dart';
import '../commonDateRangeFilter.dart';
import 'AddLiveAttendance.dart';
......@@ -158,7 +159,7 @@ class _AttendanceListScreenState extends State<AttendanceListScreen> {
// }
if (provider.response?.requestList == null ||
provider.response!.requestList!.isEmpty) {
return const Center(
return Center(
child: Text(
"No attendance records found",
style: TextStyle(
......@@ -171,7 +172,7 @@ class _AttendanceListScreenState extends State<AttendanceListScreen> {
final list = provider.response!.requestList!;
return ListView.builder(
padding: const EdgeInsets.all(8),
padding: EdgeInsets.all(8),
itemCount: list.length,
itemBuilder: (context, index) {
final item = list[index];
......@@ -186,244 +187,232 @@ class _AttendanceListScreenState extends State<AttendanceListScreen> {
context,
listen: false,
);
return Slidable(
key: ValueKey(item.id),
return Padding(
padding: const EdgeInsets.symmetric(vertical: 6),
child: ClipRRect(
borderRadius: BorderRadius.circular(20),
child: Slidable(
key: ValueKey(item.id),
// Left swipe (Reject)
startActionPane:
canSwipe
? ActionPane(
motion: const ScrollMotion(),
dragDismissible: false,
children: [
SlidableAction(
onPressed: (_) {
showRemarkSheet(
context: context,
actionType: "Reject",
onSubmit: (remark) async {
await provider
.rejectApproveAttendanceRequest(
session:
homeProvider
.session,
empId:
homeProvider
.empId,
// Left swipe (Reject)
startActionPane:
canSwipe
? ActionPane(
motion: const ScrollMotion(),
extentRatio: 0.4,
dragDismissible: false,
children: [
SlidableAction(
onPressed: (_) async {
await showRemarkSheet(
context: context,
actionType: "Reject",
onSubmit: (remark) async {
await provider.rejectApproveAttendanceRequest(
session: homeProvider.session,
empId: homeProvider.empId,
mode: widget.mode,
type: "Rejected",
remarks: remark,
id: item.id ?? "0",
);
ScaffoldMessenger.of(
context,
).showSnackBar(
const SnackBar(
content: Text(
"Attendance request rejected successfully.",
),
),
if (context.mounted) {
CustomSnackBar.showSuccess(
context: context,
message: "Attendance request rejected.",
);
// ✅ refresh here after completion
await provider.fetchAttendanceRequests(context, widget.mode);
}
},
);
// refresh list
provider
.fetchAttendanceRequests(
context,
widget.mode,
);
},
).then((_) {
provider.fetchAttendanceRequests(
context,
widget.mode,
); // or setState(() {}) if needed
});
},
backgroundColor: const Color(
0xFFFFE5E5,
),
foregroundColor: const Color(
0xFFEF3739,
),
icon: Icons.clear,
label: 'Reject',
),
],
)
: null,
backgroundColor: const Color(
0xFFFFE5E5,
),
foregroundColor: const Color(
0xFFEF3739,
),
icon: Icons.clear,
label: 'Reject',
),
],
)
: null,
// Right swipe (Approve)
endActionPane:
canSwipe
? ActionPane(
motion: const ScrollMotion(),
dragDismissible: false,
children: [
SlidableAction(
onPressed: (context) {
showRemarkSheet(
context: context,
actionType: "Approve",
onSubmit: (remark) async {
await provider
.rejectApproveAttendanceRequest(
session:
homeProvider
.session,
empId:
homeProvider
.empId,
// Right swipe (Approve)
endActionPane:
canSwipe
? ActionPane(
motion: const ScrollMotion(),
extentRatio: 0.4, // Width percentage
dragDismissible: false,
children: [
SlidableAction(
onPressed: (_) async {
await showRemarkSheet(
context: context,
actionType: "Approve",
onSubmit: (remark) async {
await provider.rejectApproveAttendanceRequest(
session: homeProvider.session,
empId: homeProvider.empId,
mode: widget.mode,
type: "Approved",
remarks: remark,
id: item.id ?? "0",
);
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text("Attendance request approved.")),
);
CustomSnackBar.showSuccess(
context: context,
message: "Attendance request approved.",
);
// refresh here after API success
await provider.fetchAttendanceRequests(context, widget.mode);
}
},
);
},
).then((_) {
provider
.fetchAttendanceRequests(
context,
widget.mode,
);
});
print(
"######################################",
);
},
backgroundColor: const Color(
0xFFE9FFE8,
),
foregroundColor: const Color(
0xFF4CB443,
),
icon: Icons.check,
label: 'Approve',
),
],
)
: null,
child: InkWell(
borderRadius: BorderRadius.circular(16),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder:
(context) =>
AttendanceRequestDetailScreen(
attendanceListId: item.id,
mode: widget.mode,
backgroundColor: const Color(
0xFFE9FFE8,
),
foregroundColor: const Color(
0xFF4CB443,
),
icon: Icons.check,
label: 'Approve',
),
),
);
},
child: Container(
margin: const EdgeInsets.symmetric(
horizontal: 8.5,
vertical: 5,
),
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 8.5,
),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
),
child: Row(
children: [
/// Left Avatar Circle
Container(
height: 48,
width: 50,
padding: const EdgeInsets.all(8.0),
decoration: BoxDecoration(
color: _getAvatarColor(item.status),
shape: BoxShape.circle,
],
)
: null,
child: InkWell(
borderRadius: BorderRadius.circular(0),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder:
(context) =>
AttendanceRequestDetailScreen(
attendanceListId: item.id,
mode: widget.mode,
),
),
child: Center(
child: Text(
getText(item.status),
style: TextStyle(
color: _getTextColor(
item.status,
);
},
child: Container(
// margin: const EdgeInsets.symmetric(
// horizontal: 0,
// vertical: 5,
// ),
padding: const EdgeInsets.symmetric(
horizontal: 14,
vertical: 12,
),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(2),
),
child: Row(
children: [
/// Left Avatar Circle
Container(
height: 48,
width: 50,
padding: const EdgeInsets.all(8.0),
decoration: BoxDecoration(
color: _getAvatarColor(item.status),
shape: BoxShape.circle,
),
child: Center(
child: Text(
getText(item.status),
style: TextStyle(
color: _getTextColor(
item.status,
),
fontSize: 14,
fontWeight: FontWeight.bold,
),
),
fontSize: 14,
fontWeight: FontWeight.bold,
),
),
),
),
const SizedBox(width: 10),
const SizedBox(width: 10),
/// Middle Section
Expanded(
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
widget.mode == "apr_lvl1"
? truncate(
item.employeeName ?? "-",
20,
)
: item.type ?? "-",
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontFamily: "JakartaRegular",
fontSize: 14,
color: AppColors.semi_black,
),
),
Row(
/// Middle Section
Expanded(
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
widget.mode == "apr_lvl1"
? item.type ?? "-"
? truncate(
item.employeeName ?? "-",
20,
)
: item.type ?? "-",
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontFamily:
"JakartaRegular",
fontFamily: "JakartaRegular",
fontSize: 14,
color:
AppColors.grey_semi,
color: AppColors.semi_black,
),
),
const SizedBox(width: 2),
Text(
" - ${item.date}" ?? "-",
style: TextStyle(
fontFamily:
"JakartaRegular",
fontSize: 14,
color:
AppColors.grey_semi,
),
Row(
children: [
Text(
widget.mode == "apr_lvl1"
? item.type ?? "-"
: item.type ?? "-",
style: TextStyle(
fontFamily:
"JakartaRegular",
fontSize: 14,
color:
AppColors.grey_semi,
),
),
const SizedBox(width: 2),
Text(
" - ${item.date}" ?? "-",
style: TextStyle(
fontFamily:
"JakartaRegular",
fontSize: 14,
color:
AppColors.grey_semi,
),
),
],
),
],
),
],
),
),
),
/// Right Status (Live / Manual)
Text(
item.attendanceType ?? "-",
textAlign: TextAlign.right,
style: TextStyle(
fontFamily: "JakartaMedium",
fontSize: 14,
color:
(item.attendanceType ?? "")
.toLowerCase() ==
"live"
? Colors.green
: Colors.orange,
),
/// Right Status (Live / Manual)
Text(
item.attendanceType ?? "-",
textAlign: TextAlign.right,
style: TextStyle(
fontFamily: "JakartaMedium",
fontSize: 14,
color:
(item.attendanceType ?? "")
.toLowerCase() ==
"live"
? Colors.green
: Colors.orange,
),
),
],
),
],
),
),
),
),
......
import 'package:flutter/material.dart';
import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart';
import '../../Notifiers/HomeScreenNotifier.dart';
import '../../Notifiers/hrmProvider/contact_provider.dart';
import '../../Utils/app_colors.dart';
import 'package:flutter_contacts/flutter_contacts.dart';
import 'package:permission_handler/permission_handler.dart';
import '../../Utils/custom_snackbar.dart';
class ContactListScreen extends StatefulWidget {
const ContactListScreen({super.key});
@override
State<ContactListScreen> createState() => _ContactListScreenState();
}
class _ContactListScreenState extends State<ContactListScreen> {
final TextEditingController _searchController = TextEditingController();
@override
void initState() {
super.initState();
final homeProv = Provider.of<HomescreenNotifier>(context, listen: false);
Future.microtask(() {
final provider = Provider.of<ContactProvider>(context, listen: false);
provider.fetchContactList(homeProv.session, homeProv.empId, 1);
});
}
void _addToContact(String name, String phoneNumber) async {
if (phoneNumber.isEmpty) {
CustomSnackBar.showError(
context: context,
message: "No phone number available",
);
return;
}
try {
// Show loading snackbar
CustomSnackBar.showLoading(
context: context,
message: "Opening contact form for $name...",
);
// Check and request contact permission
var permissionStatus = await Permission.contacts.status;
if (permissionStatus.isDenied) {
final requestedStatus = await Permission.contacts.request();
if (!requestedStatus.isGranted) {
CustomSnackBar.hide(context);
CustomSnackBar.showError(
context: context,
message: "Contact permission is required",
);
return;
}
}
if (permissionStatus.isPermanentlyDenied) {
CustomSnackBar.hide(context);
_showPermissionDialog();
return;
}
// Clean phone number
final cleanPhoneNumber = _cleanPhoneNumber(phoneNumber);
// ✅ Instead of inserting contact, open system form
final newContact = Contact()
..name = Name(first: name)
..phones = [Phone(cleanPhoneNumber)];
// 🟢 This opens the phone’s native “Add contact” screen (prefilled)
await FlutterContacts.openExternalInsert(newContact);
// Hide loading
CustomSnackBar.hide(context);
// Optional confirmation message
CustomSnackBar.showSuccess(
context: context,
message: "Contact form opened for $name",
);
} catch (e) {
debugPrint("❌ Error opening contact form: $e");
CustomSnackBar.hide(context);
CustomSnackBar.showError(
context: context,
message: "Failed to open contact form",
);
}
}
// Helper function to clean phone numbers
String _cleanPhoneNumber(String phoneNumber) {
// Remove all non-digit characters except +
String cleaned = phoneNumber.replaceAll(RegExp(r'[^\d+]'), '');
// Remove country code if it's +91 (India) and number is 10 digits
if (cleaned.startsWith('+91') && cleaned.length == 13) {
cleaned = cleaned.substring(3);
} else if (cleaned.startsWith('91') && cleaned.length == 12) {
cleaned = cleaned.substring(2);
}
// Remove leading 0 if present
if (cleaned.startsWith('0') && cleaned.length == 11) {
cleaned = cleaned.substring(1);
}
return cleaned;
}
void _showPermissionDialog() {
showDialog(
context: context,
builder: (BuildContext context) => AlertDialog(
title: const Text("Permission Required"),
content: const Text(
"Contact permission is permanently denied. Please enable it in app settings to add contacts."),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text("Cancel"),
),
TextButton(
onPressed: () {
Navigator.pop(context);
openAppSettings();
},
child: const Text("Open Settings"),
),
],
),
);
}
void _openWhatsApp(String phoneNumber) async {
if (phoneNumber.isEmpty) {
CustomSnackBar.showError(
context: context,
message: "No phone number available",
);
return;
}
final whatsappUrl = "https://wa.me/$phoneNumber";
final Uri whatsappUri = Uri.parse(whatsappUrl);
if (await canLaunchUrl(whatsappUri)) {
await launchUrl(whatsappUri);
} else {
CustomSnackBar.showError(
context: context,
message: "Could not open WhatsApp",
);
}
}
@override
Widget build(BuildContext context) {
final screenWidth = MediaQuery.of(context).size.width;
final bool isSmallScreen = screenWidth < 360;
final bool isTablet = screenWidth > 768;
return Scaffold(
backgroundColor: AppColors.scaffold_bg_color,
appBar: AppBar(
automaticallyImplyLeading: false,
backgroundColor: Colors.white,
title: Row(
children: [
InkResponse(
onTap: () => Navigator.pop(context, true),
child: SvgPicture.asset(
"assets/svg/appbar_back_button.svg",
height: isSmallScreen ? 22 : isTablet ? 28 : 25,
),
),
const SizedBox(width: 10),
Text(
"Contacts",
style: TextStyle(
fontSize: isSmallScreen ? 16 : isTablet ? 20 : 18,
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w600,
color: Colors.black87,
),
),
],
),
),
body: Consumer<ContactProvider>(
builder: (context, provider, _) {
if (provider.isLoading) {
return const Center(child: CircularProgressIndicator());
}
if (provider.contactList.isEmpty) {
return const Center(
child: Text(
"No contacts found",
style: TextStyle(
color: Colors.black54,
fontSize: 16,
fontFamily: "Plus Jakarta Sans"),
),
);
}
final filteredContacts = provider.contactList
.where((c) => (c.name ?? '')
.toLowerCase()
.contains(_searchController.text.toLowerCase()))
.toList();
return Padding(
padding: EdgeInsets.symmetric(horizontal: isSmallScreen ? 10 : 16),
child: Column(
children: [
const SizedBox(height: 12),
Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
),
child: TextField(
controller: _searchController,
onChanged: (_) => setState(() {}),
decoration: const InputDecoration(
hintText: "Search",
hintStyle: TextStyle(color: Colors.grey),
prefixIcon: Icon(Icons.search, color: Colors.grey),
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 14),
),
),
),
const SizedBox(height: 12),
Expanded(
child: ListView.builder(
itemCount: filteredContacts.length,
itemBuilder: (context, index) {
final contact = filteredContacts[index];
final canSwipe = contact.mobileNumber != null &&
contact.mobileNumber!.trim().length == 10;
return Padding(
padding: const EdgeInsets.symmetric(vertical: 6),
child: ClipRRect(
borderRadius: BorderRadius.circular(20),
child: Slidable(
key: Key(contact.mobileNumber?.toString() ?? '${contact.name}_$index'),
// Left swipe (Add to contact)
startActionPane: canSwipe
? ActionPane(
motion: const ScrollMotion(),
extentRatio: 0.4, // Width percentage
dragDismissible: false,
children: [
// This will automatically take full height of the list item
SlidableAction(
onPressed: (_) async {
_addToContact(
contact.name ?? 'Contact',
contact.mobileNumber ?? '',
);
},
backgroundColor: const Color(0xFFE5FFE5),
foregroundColor: const Color(0xFF38903c),
icon: Icons.contact_page,
label: 'Add to Contact',
autoClose: true,
),
],
)
: null,
// Right swipe (WhatsApp)
endActionPane: canSwipe
? ActionPane(
motion: const ScrollMotion(),
extentRatio: 0.4, // Width percentage
dragDismissible: false,
children: [
SlidableAction(
onPressed: (_) async {
_openWhatsApp(contact.mobileNumber ?? '');
},
backgroundColor: const Color(0xFFE9FFE8),
foregroundColor: const Color(0xFF4CB443),
icon: Icons.chat,
label: 'WhatsApp',
autoClose: true,
),
],
)
: null,
// The main content - this determines the height
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(1),
),
padding: const EdgeInsets.symmetric(
horizontal: 14, vertical: 10),
child: Row(
children: [
// 👤 Avatar
Container(
height: 42,
width: 42,
decoration: const BoxDecoration(
color: Color(0xFFE6F6FF),
shape: BoxShape.circle,
),
clipBehavior: Clip.antiAlias,
child: (contact.profileImage != null &&
contact.profileImage!.isNotEmpty)
? Image.network(
contact.profileImage!,
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) {
return const Icon(Icons.person_outline,
color: Color(0xFF2d2d2d));
},
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
return const Center(
child: CircularProgressIndicator(strokeWidth: 2),
);
},
)
: const Icon(Icons.person_outline, color: Color(0xFF2d2d2d)),
),
const SizedBox(width: 12),
// 📝 Info
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
contact.name ?? "-",
style: const TextStyle(
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w400,
fontSize: 14,
color: Colors.black87,
),
),
const SizedBox(height: 3),
Text(
"${contact.designation ?? ''}\n${contact.branchName ?? ''}",
style: TextStyle(
fontFamily: "JakartaRegular",
fontSize: 12,
fontWeight: FontWeight.w400,
color: Colors.grey.shade600,
height: 1.4,
),
),
],
),
),
// Call
if (contact.mobileNumber != null &&
contact.mobileNumber!.trim().length == 10)
IconButton(
icon: const Icon(Icons.call, color: Colors.green, size: 24),
onPressed: () async {
final phone = contact.mobileNumber!.trim();
final Uri callUri = Uri(scheme: 'tel', path: phone);
if (await canLaunchUrl(callUri)) {
await launchUrl(callUri);
} else {
debugPrint("❌ Could not launch dialer for $phone");
CustomSnackBar.showError(
context: context,
message: "Unable to open dialer",
);
}
},
),
],
),
),
),
),
);
},
),
),
],
),
);
},
),
);
}
}
\ No newline at end of file
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:generp/screens/hrm/Attendancelist.dart';
import 'package:generp/screens/hrm/ContactList.dart';
import 'package:provider/provider.dart';
import '../../Utils/app_colors.dart';
import 'AdvanceListScreen.dart';
......@@ -27,7 +28,7 @@ class _HrmdashboardScreenState extends State<HrmdashboardScreen> {
"Rewards List",
"Attendance Request List",
"Advance List",
"Casual Leave List"
"Casual Leave List",
];
// Responsive text size function
......@@ -236,7 +237,7 @@ class _HrmdashboardScreenState extends State<HrmdashboardScreen> {
return Padding(
padding: EdgeInsets.symmetric(
horizontal: getResponsivePadding(context),
horizontal: getResponsivePadding(context)-3,
vertical: 10,
),
child: Consumer<HrmAccessiblePagesProvider>(
......@@ -336,8 +337,8 @@ class _HrmdashboardScreenState extends State<HrmdashboardScreen> {
horizontal: constraints.maxWidth * 0.06,
),
margin: EdgeInsets.symmetric(
vertical: isSmallScreen ? 4 : 7,
horizontal: isSmallScreen ? 3 : 5
vertical: isSmallScreen ? 4 : 6.5,
horizontal: isSmallScreen ? 2 : 4
),
decoration: BoxDecoration(
color: Colors.white,
......@@ -435,6 +436,8 @@ class _HrmdashboardScreenState extends State<HrmdashboardScreen> {
return "Advance Payment";
case "Casual Leave List":
return "Track Casual Leave";
case "Employee Contact List":
return "Staff Contact";
default:
return "";
}
......@@ -459,6 +462,8 @@ class _HrmdashboardScreenState extends State<HrmdashboardScreen> {
return "assets/svg/hrm/advance_list_ic.svg";
case "Casual Leave List":
return "assets/svg/hrm/casual_leave_history_ic.svg";
case "Employee Contact List":
return "assets/svg/hrm/emp_contact_list.svg";
default:
return "assets/svg/hrm/groupIc.svg";
}
......@@ -534,6 +539,15 @@ class _HrmdashboardScreenState extends State<HrmdashboardScreen> {
),
);
break;
case "Employee Contact List":
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ContactListScreen(),
),
);
break;
}
}
}
\ No newline at end of file
......@@ -66,5 +66,6 @@ export 'package:generp/Notifiers/hrmProvider/rewardListProvider.dart';
export 'package:generp/Notifiers/hrmProvider/LeaveApplicationListProvider.dart';
export 'package:generp/Notifiers/hrmProvider/LeaveApplicationDetailsProvider.dart';
export 'package:generp/Notifiers/hrmProvider/CasualLeaveHistoryProvider.dart';
export 'package:generp/Notifiers/hrmProvider/contact_provider.dart';
export 'package:generp/Notifiers/hrmprovider/orgprovider.dart';
......@@ -2,6 +2,7 @@ import 'dart:io';
import 'package:dropdown_button2/dropdown_button2.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_svg/svg.dart';
import 'package:generp/Notifiers/PaymentDetailsProvider.dart';
import 'package:generp/Utils/app_colors.dart';
......@@ -474,7 +475,10 @@ class _PaymentdetailsState extends State<Paymentdetails> {
floatingActionButtonLocation:
FloatingActionButtonLocation.centerFloat,
floatingActionButton: InkWell(
onTap: () {
onTap: provider.isPaymentUpdating
? null
: () {
HapticFeedback.selectionClick();
provider.PaymentUpdateAPI(
context,
provider.Referencecontroller.text,
......@@ -484,18 +488,24 @@ class _PaymentdetailsState extends State<Paymentdetails> {
child: Container(
alignment: Alignment.center,
height: 45,
margin: EdgeInsets.only(
left: 5.0,
right: 5.0,
top: 5.0,
bottom: 5.0,
),
margin: const EdgeInsets.symmetric(horizontal: 5, vertical: 5),
decoration: BoxDecoration(
color: AppColors.app_blue, //1487C9
color: provider.isPaymentUpdating
? Colors.grey.shade400
: AppColors.app_blue,
borderRadius: BorderRadius.circular(15.0),
),
child: Center(
child: Text(
child: provider.isPaymentUpdating
? const SizedBox(
height: 22,
width: 22,
child: CircularProgressIndicator(
strokeWidth: 2.5,
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
),
)
: const Text(
"Send OTP",
textAlign: TextAlign.center,
style: TextStyle(
......@@ -506,7 +516,7 @@ class _PaymentdetailsState extends State<Paymentdetails> {
),
),
),
),
)
),
),
),
......@@ -607,207 +617,226 @@ class _PaymentdetailsState extends State<Paymentdetails> {
Future<void> showOTPSheetSheet(BuildContext context) {
return showModalBottomSheet(
useSafeArea: true,
isDismissible: true,
isDismissible: false, // Prevent dismiss while processing
isScrollControlled: true,
showDragHandle: true,
backgroundColor: Colors.white,
enableDrag: true,
enableDrag: false, // Disable drag while processing
context: context,
builder: (context) {
return StatefulBuilder(
builder: (context, setState) {
return SafeArea(
child: Consumer<Paymentdetailsprovider>(
builder: (context, provider, child) {
return Padding(
padding: EdgeInsets.only(
bottom:
MediaQuery.of(
context,
).viewInsets.bottom, // This handles keyboard
),
child: Container(
margin: EdgeInsets.only(
bottom: 15,
left: 15,
right: 15,
top: 10,
),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
return SafeArea(
child: Consumer<Paymentdetailsprovider>(
builder: (context, provider, child) {
return Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
),
child: Container(
margin: EdgeInsets.only(
bottom: 15,
left: 15,
right: 15,
top: 10,
),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Align(
alignment: Alignment.center,
child: Text(
"Enter OTP",
style: TextStyle(
color: AppColors.app_blue,
fontSize: 16,
),
),
),
SizedBox(height: 15),
Column(
children: [
Align(
Container(
alignment: Alignment.center,
child: Text(
"Enter OTP",
style: TextStyle(
color: AppColors.app_blue,
fontSize: 16,
height: 50,
margin: EdgeInsets.only(
left: 5.0,
right: 5.0,
),
child: PinCodeTextField(
appContext: context,
pastedTextStyle: TextStyle(
fontWeight: FontWeight.bold,
),
length: 4,
blinkWhenObscuring: true,
animationType: AnimationType.fade,
enabled: !provider.isVerifyingOtp, // Disable during verification
pinTheme: PinTheme(
shape: PinCodeFieldShape.underline,
borderRadius: BorderRadius.circular(16),
fieldHeight: 60,
fieldWidth: 60,
activeFillColor: AppColors.text_field_color,
activeColor: AppColors.app_blue,
selectedColor: AppColors.text_field_color,
selectedFillColor: AppColors.text_field_color,
inactiveFillColor: AppColors.text_field_color,
inactiveColor: AppColors.text_field_color,
fieldOuterPadding: EdgeInsets.only(
left: 5,
right: 5,
),
inactiveBorderWidth: 0,
activeBorderWidth: 0.5,
),
enableActiveFill: true,
keyboardType: TextInputType.number,
boxShadows: const [
BoxShadow(
offset: Offset(0, 1),
color: Colors.black12,
blurRadius: 10,
),
],
onCompleted: (String enteredCode) {
if (!provider.isVerifyingOtp) {
provider.enteredOtp = enteredCode;
provider.OTPVerifyAPI(context);
}
debugPrint("Completed");
},
onChanged: (String enteredCode) {
debugPrint(enteredCode);
provider.enteredOtp = enteredCode;
},
onSubmitted: (String enteredCode) {
if (!provider.isVerifyingOtp) {
provider.enteredOtp = enteredCode;
provider.OTPVerifyAPI(context);
}
},
enablePinAutofill: true,
useExternalAutoFillGroup: true,
beforeTextPaste: (text) {
debugPrint("Allowing to paste $text");
return true;
},
),
),
SizedBox(height: 15),
Column(
children: [
Container(
alignment: Alignment.center,
height: 50,
margin: EdgeInsets.only(
left: 5.0,
right: 5.0,
),
child: PinCodeTextField(
appContext: context,
pastedTextStyle: TextStyle(
fontWeight: FontWeight.bold,
),
length: 4,
blinkWhenObscuring: true,
animationType: AnimationType.fade,
// validator: (v) {
// if (v!.length < 3) {
// return "I'm from validator";
// } else {
// return null;
// }
// },
pinTheme: PinTheme(
shape: PinCodeFieldShape.underline,
borderRadius: BorderRadius.circular(16),
fieldHeight: 60,
fieldWidth: 60,
activeFillColor:
AppColors.text_field_color,
activeColor: AppColors.app_blue,
selectedColor: AppColors.text_field_color,
selectedFillColor:
AppColors.text_field_color,
inactiveFillColor:
AppColors.text_field_color,
inactiveColor: AppColors.text_field_color,
fieldOuterPadding: EdgeInsets.only(
left: 5,
right: 5,
// 🔁 Resend Button
Container(
alignment: Alignment.center,
height: 45,
margin: const EdgeInsets.symmetric(horizontal: 5, vertical: 5),
child: InkResponse(
onTap: provider.isResendingOtp || provider.isVerifyingOtp
? null
: () {
HapticFeedback.selectionClick();
provider.ResendOtpAPI(context);
},
child: Center(
child: provider.isResendingOtp
? Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: const [
SizedBox(
height: 18,
width: 18,
child: CircularProgressIndicator(
strokeWidth: 2.2,
valueColor: AlwaysStoppedAnimation<Color>(Colors.grey),
),
),
inactiveBorderWidth: 0,
activeBorderWidth: 0.5,
),
enableActiveFill: true,
keyboardType: TextInputType.number,
boxShadows: const [
BoxShadow(
offset: Offset(0, 1),
color: Colors.black12,
blurRadius: 10,
SizedBox(width: 8),
Text(
"Sending...",
style: TextStyle(color: Colors.grey),
),
],
onCompleted: (String enteredCode) {
provider.enteredOtp = enteredCode;
// clearText = true;
provider.OTPVerifyAPI(context);
debugPrint("Completed");
},
// onTap: () {
// print("Pressed");
// },
onChanged: (String enteredCode) {
debugPrint(enteredCode);
provider.enteredOtp = enteredCode;
},
onSubmitted: (String enteredCode) {
provider.enteredOtp = enteredCode;
// clearText = true;
// Verify_otp();
},
enablePinAutofill: true,
useExternalAutoFillGroup: true,
beforeTextPaste: (text) {
debugPrint("Allowing to paste $text");
//if you return true then it will show the paste confirmation dialog. Otherwise if false, then nothing will happen.
//but you can show anything you want here, like your pop up saying wrong paste format or etc
return true;
},
)
: const Text(
"Resend",
style: TextStyle(fontWeight: FontWeight.w300),
),
),
SizedBox(height: 15),
Container(
alignment: Alignment.center,
height: 45,
margin: EdgeInsets.only(
left: 5.0,
right: 5.0,
top: 5.0,
bottom: 5.0,
),
child: InkResponse(
onTap: () {
provider.ResendOtpAPI(context);
},
child: Center(
child: Text(
"Resend",
style: TextStyle(
fontWeight: FontWeight.w300,
),
),
// Submit Button
InkResponse(
onTap: provider.isVerifyingOtp || provider.enteredOtp.length != 4
? null
: () {
HapticFeedback.selectionClick();
provider.OTPVerifyAPI(context);
},
child: Container(
alignment: Alignment.center,
height: 45,
margin: const EdgeInsets.symmetric(horizontal: 5, vertical: 5),
decoration: BoxDecoration(
color: provider.isVerifyingOtp
? Colors.grey.shade400
: (provider.enteredOtp.length == 4 ? AppColors.app_blue : Colors.grey.shade300),
borderRadius: BorderRadius.circular(15.0),
),
child: Center(
child: provider.isVerifyingOtp
? Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: const [
SizedBox(
height: 20,
width: 20,
child: CircularProgressIndicator(
strokeWidth: 2.3,
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
),
),
),
),
),
InkResponse(
onTap: () {
Navigator.of(context).pop(false);
provider.OTPVerifyAPI(context);
},
child: Container(
alignment: Alignment.center,
height: 45,
margin: EdgeInsets.only(
left: 5.0,
right: 5.0,
top: 5.0,
bottom: 5.0,
),
decoration: BoxDecoration(
color: AppColors.app_blue, //1487C9
borderRadius: BorderRadius.circular(15.0),
),
child: Center(
child: Text(
"Submit",
textAlign: TextAlign.center,
SizedBox(width: 8),
Text(
"Verifying...",
style: TextStyle(
fontSize: 15,
fontFamily: "JakartaMedium",
color: Colors.white,
),
),
],
)
: Text(
"Submit",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 15,
fontFamily: "JakartaMedium",
color: provider.enteredOtp.length == 4 ? Colors.white : Colors.grey,
),
),
),
],
),
),
],
),
),
],
),
);
},
),
);
},
),
),
);
},
),
);
},
);
}
Future<void> _addContactSheet(BuildContext context) {
final controllersNames = [
"Name",
......
......@@ -29,6 +29,7 @@ import 'package:generp/Models/financeModels/paymentRequisitionPaymentsListRespon
import 'package:generp/Models/hrmModels/advanceListResponse.dart';
import 'package:generp/Models/hrmModels/attendanceRequestListResponse.dart';
import 'package:generp/Models/hrmModels/casualLeaveHistoryResponse.dart';
import 'package:generp/Models/hrmModels/contactListResponse.dart';
import 'package:generp/Models/hrmModels/jobDescriptionResponse.dart';
import 'package:generp/Models/hrmModels/leaveApplicationDetailsResponse.dart';
import 'package:generp/Models/hrmModels/leaveApplicationLIstResponse.dart';
......@@ -3655,6 +3656,7 @@ class ApiCalling {
if (res != null) {
print(data);
debugPrint("Submit New Leads ${res.body}");
return crmNewLeadsProspectsSubmitResponse.fromJson(
jsonDecode(res.body),
);
......@@ -3819,7 +3821,7 @@ class ApiCalling {
final res = await post(data, crmLeadListFilterSubmitUrl, {});
if (res != null) {
print("Lead Filter:$data");
debugPrint(res.body);
debugPrint(" Lead list data :${res.body}");
return SubmitLeadListFilterResponse.fromJson(jsonDecode(res.body));
} else {
debugPrint("Null Response");
......@@ -5637,6 +5639,33 @@ class ApiCalling {
}
}
// Contact list
static Future<ContactListResponse?> contactListAPI(
session,
empId,
pageNumber
) async {
debugPrint("🔥🔥🔥🔥🔥🔥🔥Response");
try {
Map<String, String> data = {
'session_id': (session).toString(),
'emp_id': (empId).toString(),
};
final res = await post(data, EmployeeContactListUrl, {});
if (res != null) {
print("Request Data: $data");
debugPrint("🔥🔥🔥🔥🔥🔥🔥 Response: ${res.body}");
return ContactListResponse.fromJson(jsonDecode(res.body));
} else {
debugPrint("Null Response");
return null;
}
} catch (e) {
debugPrint('API Error: $e');
return null;
}
}
// static Future<CommonResponse?> TpcIssueListApprovalAPI(
// empId,
// session,
......
......@@ -205,7 +205,7 @@ const LeaveRequestAdditionUrl ="${baseUrl_test}add_leave_request";
const LeaveRequestRejectAprroveUrl ="${baseUrl_test}leaves_approve_reject";
const CasuaLeaveHistoryUrl ="${baseUrl_test}casual_leave_history";
const EmployeeContactListUrl ="${baseUrl_test}employee_contact_list";
const AdvanceListUrl ="${baseUrl_test}advance_list";
......
......@@ -574,6 +574,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.4.1"
flutter_contacts:
dependency: "direct main"
description:
name: flutter_contacts
sha256: "388d32cd33f16640ee169570128c933b45f3259bddbfae7a100bb49e5ffea9ae"
url: "https://pub.dev"
source: hosted
version: "1.1.9+2"
flutter_download_manager:
dependency: "direct main"
description:
......
......@@ -93,6 +93,7 @@ dependencies:
file_picker: ^8.0.0
flutter_html: ^3.0.0
photo_view: ^0.14.0
flutter_contacts: ^1.1.9+2
dev_dependencies:
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment