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'; ...@@ -12,6 +12,7 @@ import '../../Notifiers/HomeScreenNotifier.dart';
import '../../Notifiers/hrmProvider/attendanceListProvider.dart'; import '../../Notifiers/hrmProvider/attendanceListProvider.dart';
import '../../Utils/app_colors.dart'; import '../../Utils/app_colors.dart';
import '../../Utils/commonWidgets.dart'; import '../../Utils/commonWidgets.dart';
import '../../Utils/custom_snackbar.dart';
import '../CommonFilter2.dart'; import '../CommonFilter2.dart';
import '../commonDateRangeFilter.dart'; import '../commonDateRangeFilter.dart';
import 'AddLiveAttendance.dart'; import 'AddLiveAttendance.dart';
...@@ -158,7 +159,7 @@ class _AttendanceListScreenState extends State<AttendanceListScreen> { ...@@ -158,7 +159,7 @@ class _AttendanceListScreenState extends State<AttendanceListScreen> {
// } // }
if (provider.response?.requestList == null || if (provider.response?.requestList == null ||
provider.response!.requestList!.isEmpty) { provider.response!.requestList!.isEmpty) {
return const Center( return Center(
child: Text( child: Text(
"No attendance records found", "No attendance records found",
style: TextStyle( style: TextStyle(
...@@ -171,7 +172,7 @@ class _AttendanceListScreenState extends State<AttendanceListScreen> { ...@@ -171,7 +172,7 @@ class _AttendanceListScreenState extends State<AttendanceListScreen> {
final list = provider.response!.requestList!; final list = provider.response!.requestList!;
return ListView.builder( return ListView.builder(
padding: const EdgeInsets.all(8), padding: EdgeInsets.all(8),
itemCount: list.length, itemCount: list.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final item = list[index]; final item = list[index];
...@@ -186,244 +187,232 @@ class _AttendanceListScreenState extends State<AttendanceListScreen> { ...@@ -186,244 +187,232 @@ class _AttendanceListScreenState extends State<AttendanceListScreen> {
context, context,
listen: false, listen: false,
); );
return Slidable( return Padding(
key: ValueKey(item.id), padding: const EdgeInsets.symmetric(vertical: 6),
child: ClipRRect(
borderRadius: BorderRadius.circular(20),
child: Slidable(
key: ValueKey(item.id),
// Left swipe (Reject) // Left swipe (Reject)
startActionPane: startActionPane:
canSwipe canSwipe
? ActionPane( ? ActionPane(
motion: const ScrollMotion(), motion: const ScrollMotion(),
dragDismissible: false, extentRatio: 0.4,
children: [ dragDismissible: false,
SlidableAction( children: [
onPressed: (_) { SlidableAction(
showRemarkSheet( onPressed: (_) async {
context: context, await showRemarkSheet(
actionType: "Reject", context: context,
onSubmit: (remark) async { actionType: "Reject",
await provider onSubmit: (remark) async {
.rejectApproveAttendanceRequest( await provider.rejectApproveAttendanceRequest(
session: session: homeProvider.session,
homeProvider empId: homeProvider.empId,
.session,
empId:
homeProvider
.empId,
mode: widget.mode, mode: widget.mode,
type: "Rejected", type: "Rejected",
remarks: remark, remarks: remark,
id: item.id ?? "0", id: item.id ?? "0",
); );
ScaffoldMessenger.of( if (context.mounted) {
context, CustomSnackBar.showSuccess(
).showSnackBar( context: context,
const SnackBar( message: "Attendance request rejected.",
content: Text( );
"Attendance request rejected successfully.", // ✅ refresh here after completion
), await provider.fetchAttendanceRequests(context, widget.mode);
), }
},
); );
// refresh list
provider
.fetchAttendanceRequests(
context,
widget.mode,
);
}, },
).then((_) { backgroundColor: const Color(
provider.fetchAttendanceRequests( 0xFFFFE5E5,
context, ),
widget.mode, foregroundColor: const Color(
); // or setState(() {}) if needed 0xFFEF3739,
}); ),
}, icon: Icons.clear,
backgroundColor: const Color( label: 'Reject',
0xFFFFE5E5, ),
), ],
foregroundColor: const Color( )
0xFFEF3739, : null,
),
icon: Icons.clear,
label: 'Reject',
),
],
)
: null,
// Right swipe (Approve) // Right swipe (Approve)
endActionPane: endActionPane:
canSwipe canSwipe
? ActionPane( ? ActionPane(
motion: const ScrollMotion(), motion: const ScrollMotion(),
dragDismissible: false, extentRatio: 0.4, // Width percentage
children: [ dragDismissible: false,
SlidableAction( children: [
onPressed: (context) { SlidableAction(
showRemarkSheet( onPressed: (_) async {
context: context, await showRemarkSheet(
actionType: "Approve", context: context,
onSubmit: (remark) async { actionType: "Approve",
await provider onSubmit: (remark) async {
.rejectApproveAttendanceRequest( await provider.rejectApproveAttendanceRequest(
session: session: homeProvider.session,
homeProvider empId: homeProvider.empId,
.session,
empId:
homeProvider
.empId,
mode: widget.mode, mode: widget.mode,
type: "Approved", type: "Approved",
remarks: remark, remarks: remark,
id: item.id ?? "0", 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((_) { backgroundColor: const Color(
provider 0xFFE9FFE8,
.fetchAttendanceRequests( ),
context, foregroundColor: const Color(
widget.mode, 0xFF4CB443,
); ),
}); icon: Icons.check,
print( label: 'Approve',
"######################################",
);
},
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,
), ),
), ],
); )
}, : null,
child: Container(
margin: const EdgeInsets.symmetric( child: InkWell(
horizontal: 8.5, borderRadius: BorderRadius.circular(0),
vertical: 5, onTap: () {
), Navigator.push(
padding: const EdgeInsets.symmetric( context,
horizontal: 12, MaterialPageRoute(
vertical: 8.5, builder:
), (context) =>
decoration: BoxDecoration( AttendanceRequestDetailScreen(
color: Colors.white, attendanceListId: item.id,
borderRadius: BorderRadius.circular(16), mode: widget.mode,
), ),
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), child: Container(
style: TextStyle( // margin: const EdgeInsets.symmetric(
color: _getTextColor( // horizontal: 0,
item.status, // 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 /// Middle Section
Expanded( Expanded(
child: Column( child: Column(
crossAxisAlignment: crossAxisAlignment:
CrossAxisAlignment.start, 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(
children: [ children: [
Text( Text(
widget.mode == "apr_lvl1" widget.mode == "apr_lvl1"
? item.type ?? "-" ? truncate(
item.employeeName ?? "-",
20,
)
: item.type ?? "-", : item.type ?? "-",
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle( style: TextStyle(
fontFamily: fontFamily: "JakartaRegular",
"JakartaRegular",
fontSize: 14, fontSize: 14,
color: color: AppColors.semi_black,
AppColors.grey_semi,
), ),
), ),
const SizedBox(width: 2), Row(
Text( children: [
" - ${item.date}" ?? "-", Text(
style: TextStyle( widget.mode == "apr_lvl1"
fontFamily: ? item.type ?? "-"
"JakartaRegular", : item.type ?? "-",
fontSize: 14, style: TextStyle(
color: fontFamily:
AppColors.grey_semi, "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) /// Right Status (Live / Manual)
Text( Text(
item.attendanceType ?? "-", item.attendanceType ?? "-",
textAlign: TextAlign.right, textAlign: TextAlign.right,
style: TextStyle( style: TextStyle(
fontFamily: "JakartaMedium", fontFamily: "JakartaMedium",
fontSize: 14, fontSize: 14,
color: color:
(item.attendanceType ?? "") (item.attendanceType ?? "")
.toLowerCase() == .toLowerCase() ==
"live" "live"
? Colors.green ? Colors.green
: Colors.orange, : 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/material.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:generp/screens/hrm/Attendancelist.dart'; import 'package:generp/screens/hrm/Attendancelist.dart';
import 'package:generp/screens/hrm/ContactList.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../../Utils/app_colors.dart'; import '../../Utils/app_colors.dart';
import 'AdvanceListScreen.dart'; import 'AdvanceListScreen.dart';
...@@ -27,7 +28,7 @@ class _HrmdashboardScreenState extends State<HrmdashboardScreen> { ...@@ -27,7 +28,7 @@ class _HrmdashboardScreenState extends State<HrmdashboardScreen> {
"Rewards List", "Rewards List",
"Attendance Request List", "Attendance Request List",
"Advance List", "Advance List",
"Casual Leave List" "Casual Leave List",
]; ];
// Responsive text size function // Responsive text size function
...@@ -236,7 +237,7 @@ class _HrmdashboardScreenState extends State<HrmdashboardScreen> { ...@@ -236,7 +237,7 @@ class _HrmdashboardScreenState extends State<HrmdashboardScreen> {
return Padding( return Padding(
padding: EdgeInsets.symmetric( padding: EdgeInsets.symmetric(
horizontal: getResponsivePadding(context), horizontal: getResponsivePadding(context)-3,
vertical: 10, vertical: 10,
), ),
child: Consumer<HrmAccessiblePagesProvider>( child: Consumer<HrmAccessiblePagesProvider>(
...@@ -336,8 +337,8 @@ class _HrmdashboardScreenState extends State<HrmdashboardScreen> { ...@@ -336,8 +337,8 @@ class _HrmdashboardScreenState extends State<HrmdashboardScreen> {
horizontal: constraints.maxWidth * 0.06, horizontal: constraints.maxWidth * 0.06,
), ),
margin: EdgeInsets.symmetric( margin: EdgeInsets.symmetric(
vertical: isSmallScreen ? 4 : 7, vertical: isSmallScreen ? 4 : 6.5,
horizontal: isSmallScreen ? 3 : 5 horizontal: isSmallScreen ? 2 : 4
), ),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: Colors.white,
...@@ -435,6 +436,8 @@ class _HrmdashboardScreenState extends State<HrmdashboardScreen> { ...@@ -435,6 +436,8 @@ class _HrmdashboardScreenState extends State<HrmdashboardScreen> {
return "Advance Payment"; return "Advance Payment";
case "Casual Leave List": case "Casual Leave List":
return "Track Casual Leave"; return "Track Casual Leave";
case "Employee Contact List":
return "Staff Contact";
default: default:
return ""; return "";
} }
...@@ -459,6 +462,8 @@ class _HrmdashboardScreenState extends State<HrmdashboardScreen> { ...@@ -459,6 +462,8 @@ class _HrmdashboardScreenState extends State<HrmdashboardScreen> {
return "assets/svg/hrm/advance_list_ic.svg"; return "assets/svg/hrm/advance_list_ic.svg";
case "Casual Leave List": case "Casual Leave List":
return "assets/svg/hrm/casual_leave_history_ic.svg"; return "assets/svg/hrm/casual_leave_history_ic.svg";
case "Employee Contact List":
return "assets/svg/hrm/emp_contact_list.svg";
default: default:
return "assets/svg/hrm/groupIc.svg"; return "assets/svg/hrm/groupIc.svg";
} }
...@@ -534,6 +539,15 @@ class _HrmdashboardScreenState extends State<HrmdashboardScreen> { ...@@ -534,6 +539,15 @@ class _HrmdashboardScreenState extends State<HrmdashboardScreen> {
), ),
); );
break; 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'; ...@@ -66,5 +66,6 @@ export 'package:generp/Notifiers/hrmProvider/rewardListProvider.dart';
export 'package:generp/Notifiers/hrmProvider/LeaveApplicationListProvider.dart'; export 'package:generp/Notifiers/hrmProvider/LeaveApplicationListProvider.dart';
export 'package:generp/Notifiers/hrmProvider/LeaveApplicationDetailsProvider.dart'; export 'package:generp/Notifiers/hrmProvider/LeaveApplicationDetailsProvider.dart';
export 'package:generp/Notifiers/hrmProvider/CasualLeaveHistoryProvider.dart'; export 'package:generp/Notifiers/hrmProvider/CasualLeaveHistoryProvider.dart';
export 'package:generp/Notifiers/hrmProvider/contact_provider.dart';
export 'package:generp/Notifiers/hrmprovider/orgprovider.dart'; export 'package:generp/Notifiers/hrmprovider/orgprovider.dart';
...@@ -2,6 +2,7 @@ import 'dart:io'; ...@@ -2,6 +2,7 @@ import 'dart:io';
import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:dropdown_button2/dropdown_button2.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:generp/Notifiers/PaymentDetailsProvider.dart'; import 'package:generp/Notifiers/PaymentDetailsProvider.dart';
import 'package:generp/Utils/app_colors.dart'; import 'package:generp/Utils/app_colors.dart';
...@@ -474,7 +475,10 @@ class _PaymentdetailsState extends State<Paymentdetails> { ...@@ -474,7 +475,10 @@ class _PaymentdetailsState extends State<Paymentdetails> {
floatingActionButtonLocation: floatingActionButtonLocation:
FloatingActionButtonLocation.centerFloat, FloatingActionButtonLocation.centerFloat,
floatingActionButton: InkWell( floatingActionButton: InkWell(
onTap: () { onTap: provider.isPaymentUpdating
? null
: () {
HapticFeedback.selectionClick();
provider.PaymentUpdateAPI( provider.PaymentUpdateAPI(
context, context,
provider.Referencecontroller.text, provider.Referencecontroller.text,
...@@ -484,18 +488,24 @@ class _PaymentdetailsState extends State<Paymentdetails> { ...@@ -484,18 +488,24 @@ class _PaymentdetailsState extends State<Paymentdetails> {
child: Container( child: Container(
alignment: Alignment.center, alignment: Alignment.center,
height: 45, height: 45,
margin: EdgeInsets.only( margin: const EdgeInsets.symmetric(horizontal: 5, vertical: 5),
left: 5.0,
right: 5.0,
top: 5.0,
bottom: 5.0,
),
decoration: BoxDecoration( decoration: BoxDecoration(
color: AppColors.app_blue, //1487C9 color: provider.isPaymentUpdating
? Colors.grey.shade400
: AppColors.app_blue,
borderRadius: BorderRadius.circular(15.0), borderRadius: BorderRadius.circular(15.0),
), ),
child: Center( 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", "Send OTP",
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: TextStyle( style: TextStyle(
...@@ -506,7 +516,7 @@ class _PaymentdetailsState extends State<Paymentdetails> { ...@@ -506,7 +516,7 @@ class _PaymentdetailsState extends State<Paymentdetails> {
), ),
), ),
), ),
), )
), ),
), ),
), ),
...@@ -607,207 +617,226 @@ class _PaymentdetailsState extends State<Paymentdetails> { ...@@ -607,207 +617,226 @@ class _PaymentdetailsState extends State<Paymentdetails> {
Future<void> showOTPSheetSheet(BuildContext context) { Future<void> showOTPSheetSheet(BuildContext context) {
return showModalBottomSheet( return showModalBottomSheet(
useSafeArea: true, useSafeArea: true,
isDismissible: true, isDismissible: false, // Prevent dismiss while processing
isScrollControlled: true, isScrollControlled: true,
showDragHandle: true, showDragHandle: true,
backgroundColor: Colors.white, backgroundColor: Colors.white,
enableDrag: true, enableDrag: false, // Disable drag while processing
context: context, context: context,
builder: (context) { builder: (context) {
return StatefulBuilder( return SafeArea(
builder: (context, setState) { child: Consumer<Paymentdetailsprovider>(
return SafeArea( builder: (context, provider, child) {
child: Consumer<Paymentdetailsprovider>( return Padding(
builder: (context, provider, child) { padding: EdgeInsets.only(
return Padding( bottom: MediaQuery.of(context).viewInsets.bottom,
padding: EdgeInsets.only( ),
bottom: child: Container(
MediaQuery.of( margin: EdgeInsets.only(
context, bottom: 15,
).viewInsets.bottom, // This handles keyboard left: 15,
), right: 15,
child: Container( top: 10,
margin: EdgeInsets.only( ),
bottom: 15, child: SingleChildScrollView(
left: 15, child: Column(
right: 15, crossAxisAlignment: CrossAxisAlignment.start,
top: 10, mainAxisSize: MainAxisSize.min,
), children: [
Align(
child: SingleChildScrollView( alignment: Alignment.center,
child: Column( child: Text(
crossAxisAlignment: CrossAxisAlignment.start, "Enter OTP",
mainAxisSize: MainAxisSize.min, style: TextStyle(
color: AppColors.app_blue,
fontSize: 16,
),
),
),
SizedBox(height: 15),
Column(
children: [ children: [
Align( Container(
alignment: Alignment.center, alignment: Alignment.center,
child: Text( height: 50,
"Enter OTP", margin: EdgeInsets.only(
style: TextStyle( left: 5.0,
color: AppColors.app_blue, right: 5.0,
fontSize: 16, ),
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), SizedBox(height: 15),
Column(
children: [
Container(
alignment: Alignment.center,
height: 50, // 🔁 Resend Button
margin: EdgeInsets.only( Container(
left: 5.0, alignment: Alignment.center,
right: 5.0, height: 45,
), margin: const EdgeInsets.symmetric(horizontal: 5, vertical: 5),
child: PinCodeTextField( child: InkResponse(
appContext: context, onTap: provider.isResendingOtp || provider.isVerifyingOtp
pastedTextStyle: TextStyle( ? null
fontWeight: FontWeight.bold, : () {
), HapticFeedback.selectionClick();
length: 4, provider.ResendOtpAPI(context);
blinkWhenObscuring: true, },
child: Center(
animationType: AnimationType.fade, child: provider.isResendingOtp
// validator: (v) { ? Row(
// if (v!.length < 3) { mainAxisAlignment: MainAxisAlignment.center,
// return "I'm from validator"; mainAxisSize: MainAxisSize.min,
// } else { children: const [
// return null; SizedBox(
// } height: 18,
// }, width: 18,
pinTheme: PinTheme( child: CircularProgressIndicator(
shape: PinCodeFieldShape.underline, strokeWidth: 2.2,
borderRadius: BorderRadius.circular(16), valueColor: AlwaysStoppedAnimation<Color>(Colors.grey),
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, SizedBox(width: 8),
activeBorderWidth: 0.5, Text(
), "Sending...",
enableActiveFill: true, style: TextStyle(color: Colors.grey),
keyboardType: TextInputType.number,
boxShadows: const [
BoxShadow(
offset: Offset(0, 1),
color: Colors.black12,
blurRadius: 10,
), ),
], ],
onCompleted: (String enteredCode) { )
provider.enteredOtp = enteredCode; : const Text(
// clearText = true; "Resend",
provider.OTPVerifyAPI(context); style: TextStyle(fontWeight: FontWeight.w300),
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;
},
), ),
), ),
SizedBox(height: 15), ),
Container( ),
alignment: Alignment.center,
height: 45, // Submit Button
margin: EdgeInsets.only( InkResponse(
left: 5.0, onTap: provider.isVerifyingOtp || provider.enteredOtp.length != 4
right: 5.0, ? null
top: 5.0, : () {
bottom: 5.0, HapticFeedback.selectionClick();
), provider.OTPVerifyAPI(context);
child: InkResponse( },
onTap: () { child: Container(
provider.ResendOtpAPI(context); alignment: Alignment.center,
}, height: 45,
child: Center( margin: const EdgeInsets.symmetric(horizontal: 5, vertical: 5),
child: Text( decoration: BoxDecoration(
"Resend", color: provider.isVerifyingOtp
style: TextStyle( ? Colors.grey.shade400
fontWeight: FontWeight.w300, : (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),
), ),
), ),
), SizedBox(width: 8),
), Text(
), "Verifying...",
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,
style: TextStyle( style: TextStyle(
fontSize: 15, fontSize: 15,
fontFamily: "JakartaMedium", fontFamily: "JakartaMedium",
color: Colors.white, 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) { Future<void> _addContactSheet(BuildContext context) {
final controllersNames = [ final controllersNames = [
"Name", "Name",
......
...@@ -29,6 +29,7 @@ import 'package:generp/Models/financeModels/paymentRequisitionPaymentsListRespon ...@@ -29,6 +29,7 @@ import 'package:generp/Models/financeModels/paymentRequisitionPaymentsListRespon
import 'package:generp/Models/hrmModels/advanceListResponse.dart'; import 'package:generp/Models/hrmModels/advanceListResponse.dart';
import 'package:generp/Models/hrmModels/attendanceRequestListResponse.dart'; import 'package:generp/Models/hrmModels/attendanceRequestListResponse.dart';
import 'package:generp/Models/hrmModels/casualLeaveHistoryResponse.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/jobDescriptionResponse.dart';
import 'package:generp/Models/hrmModels/leaveApplicationDetailsResponse.dart'; import 'package:generp/Models/hrmModels/leaveApplicationDetailsResponse.dart';
import 'package:generp/Models/hrmModels/leaveApplicationLIstResponse.dart'; import 'package:generp/Models/hrmModels/leaveApplicationLIstResponse.dart';
...@@ -3655,6 +3656,7 @@ class ApiCalling { ...@@ -3655,6 +3656,7 @@ class ApiCalling {
if (res != null) { if (res != null) {
print(data); print(data);
debugPrint("Submit New Leads ${res.body}"); debugPrint("Submit New Leads ${res.body}");
return crmNewLeadsProspectsSubmitResponse.fromJson( return crmNewLeadsProspectsSubmitResponse.fromJson(
jsonDecode(res.body), jsonDecode(res.body),
); );
...@@ -3819,7 +3821,7 @@ class ApiCalling { ...@@ -3819,7 +3821,7 @@ class ApiCalling {
final res = await post(data, crmLeadListFilterSubmitUrl, {}); final res = await post(data, crmLeadListFilterSubmitUrl, {});
if (res != null) { if (res != null) {
print("Lead Filter:$data"); print("Lead Filter:$data");
debugPrint(res.body); debugPrint(" Lead list data :${res.body}");
return SubmitLeadListFilterResponse.fromJson(jsonDecode(res.body)); return SubmitLeadListFilterResponse.fromJson(jsonDecode(res.body));
} else { } else {
debugPrint("Null Response"); debugPrint("Null Response");
...@@ -5637,6 +5639,33 @@ class ApiCalling { ...@@ -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( // static Future<CommonResponse?> TpcIssueListApprovalAPI(
// empId, // empId,
// session, // session,
......
...@@ -205,7 +205,7 @@ const LeaveRequestAdditionUrl ="${baseUrl_test}add_leave_request"; ...@@ -205,7 +205,7 @@ const LeaveRequestAdditionUrl ="${baseUrl_test}add_leave_request";
const LeaveRequestRejectAprroveUrl ="${baseUrl_test}leaves_approve_reject"; const LeaveRequestRejectAprroveUrl ="${baseUrl_test}leaves_approve_reject";
const CasuaLeaveHistoryUrl ="${baseUrl_test}casual_leave_history"; const CasuaLeaveHistoryUrl ="${baseUrl_test}casual_leave_history";
const EmployeeContactListUrl ="${baseUrl_test}employee_contact_list";
const AdvanceListUrl ="${baseUrl_test}advance_list"; const AdvanceListUrl ="${baseUrl_test}advance_list";
......
...@@ -574,6 +574,14 @@ packages: ...@@ -574,6 +574,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.4.1" 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: flutter_download_manager:
dependency: "direct main" dependency: "direct main"
description: description:
......
...@@ -93,6 +93,7 @@ dependencies: ...@@ -93,6 +93,7 @@ dependencies:
file_picker: ^8.0.0 file_picker: ^8.0.0
flutter_html: ^3.0.0 flutter_html: ^3.0.0
photo_view: ^0.14.0 photo_view: ^0.14.0
flutter_contacts: ^1.1.9+2
dev_dependencies: 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