......@@ -55,12 +55,36 @@ class _AddLiveAttendanceScreenState extends State<AddLiveAttendanceScreen> {
}
Future<void> _autoFetchLocation() async {
String loc = await getCurrentLocation();
Position position = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.high,
);
// Save raw coordinates separately (for submission)
final coords = "${position.latitude},${position.longitude}";
// Convert to address for display
final placemarks =
await placemarkFromCoordinates(position.latitude, position.longitude);
String displayAddress;
if (placemarks.isNotEmpty) {
final place = placemarks.first;
displayAddress =
"${place.name}, ${place.locality}, ${place.administrativeArea}, ${place.country}";
} else {
displayAddress = coords; // fallback
}
setState(() {
locationController.text = loc;
locationController.text = displayAddress; // what user sees
_rawCoordinates = coords; // keep coords hidden for backend
});
}
// Add this field at the top of your State class:
String? _rawCoordinates;
Future<String> getCurrentLocation() async {
try {
LocationPermission permission = await Geolocator.checkPermission();
......@@ -164,7 +188,7 @@ class _AddLiveAttendanceScreenState extends State<AddLiveAttendanceScreen> {
context,
process: "Live",
type: selectedType ?? "",
loc: locationController.text,
loc: _rawCoordinates ?? "", // send actual coordinates
checkDate: DateTime.now().toString().split(" ").first,
checkInTime:
selectedType == "Check In" ? TimeOfDay.now().format(context) : null,
......@@ -198,7 +222,9 @@ class _AddLiveAttendanceScreenState extends State<AddLiveAttendanceScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
return SafeArea(
top: false,
child: Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
automaticallyImplyLeading: false,
......@@ -404,6 +430,7 @@ class _AddLiveAttendanceScreenState extends State<AddLiveAttendanceScreen> {
],
),
),
),
);
}
......
......@@ -168,7 +168,7 @@ class _AddManualAttendanceScreenState extends State<AddManualAttendanceScreen> {
}
void _submitForm(BuildContext context) async {
// reset errors first
// Reset errors first
dateError = null;
typeError = null;
checkInTimeError = checkInLocError = checkInDescError = checkInProofError = null;
......@@ -176,15 +176,27 @@ class _AddManualAttendanceScreenState extends State<AddManualAttendanceScreen> {
final provider = Provider.of<Attendancelistprovider>(context, listen: false);
// --- Date Validation ---
// --- Date Validation (allow today, yesterday, day before yesterday) ---
if (provider.dateController.text.isEmpty) {
dateError = "Please select a date";
} else {
try {
final enteredDate = DateFormat("dd MMM yyyy").parse(provider.dateController.text);
provider.setSelectedDate(enteredDate);
if (!provider.isDateValid()) {
dateError = "Date must be today or yesterday";
final today = DateTime.now();
final yesterday = today.subtract(const Duration(days: 1));
final dayBeforeYesterday = today.subtract(const Duration(days: 2));
// Normalize dates (ignore time part)
bool isValid = enteredDate.year == today.year &&
enteredDate.month == today.month &&
(enteredDate.day == today.day ||
enteredDate.day == yesterday.day ||
enteredDate.day == dayBeforeYesterday.day);
if (!isValid) {
dateError = "Date must be today, yesterday, or the day before yesterday";
}
} catch (e) {
dateError = "Invalid date format (use dd MMM yyyy)";
......@@ -225,7 +237,19 @@ class _AddManualAttendanceScreenState extends State<AddManualAttendanceScreen> {
checkOutDescError,
checkOutProofError
].any((e) => e != null)) {
setState(() {});
setState(() {}); // refresh UI to show error messages
return;
}
// --- Format date for server ---
String formattedDate = "";
try {
final parsedDate = DateFormat("dd MMM yyyy").parse(provider.dateController.text);
formattedDate = DateFormat("yyyy-MM-dd").format(parsedDate);
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Error formatting date: $e")),
);
return;
}
......@@ -268,7 +292,7 @@ class _AddManualAttendanceScreenState extends State<AddManualAttendanceScreen> {
: selectedType == "Check Out"
? checkOutLocation.text
: "${checkInLocation.text}, ${checkOutLocation.text}",
checkDate: provider.dateController.text,
checkDate: formattedDate,
checkInTime: finalCheckInTime,
checkInLoc: finalCheckInLoc,
checkInProof: finalCheckInProof,
......@@ -278,14 +302,13 @@ class _AddManualAttendanceScreenState extends State<AddManualAttendanceScreen> {
note: finalNote,
);
// Check the response from provider
// --- Response handling ---
if (provider.addResponse != null && provider.addResponse!.error == "0") {
// Success case
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(provider.addResponse!.message ?? "Attendance Submitted Successfully")),
);
// --- Reset fields ---
// Reset fields
setState(() {
selectedType = null;
provider.dateController.clear();
......@@ -301,19 +324,20 @@ class _AddManualAttendanceScreenState extends State<AddManualAttendanceScreen> {
_fetchInitialLocation();
} else {
// Error case - show appropriate message
String errorMessage = provider.errorMessage ?? "Failed to submit attendance";
// Handle specific server error for Check Out without Check In
if (errorMessage.contains("Check In is not Available")) {
errorMessage = "Cannot submit Check Out without a Check In record for this date";
}
if (errorMessage.contains("2")){
errorMessage = "Only One manual Request can be added in a month !";
}
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(errorMessage), backgroundColor: Colors.red),
);
}
}
// it's date picker need to take day before yesterday, yesterday and today
......
This diff is collapsed.
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:generp/screens/hrm/OrganizationStructureScreen.dart';
import 'package:generp/screens/hrm/RewardListScreen.dart';
import 'package:generp/screens/hrm/Attendancelist.dart';
import 'package:provider/provider.dart';
import '../../Utils/app_colors.dart';
import 'AttendanceRequestDetail.dart';
import 'LeaveApplicationScreen.dart';
import 'TourExpensesListScreen.dart';
import 'attendancelist.dart';
import 'RewardListScreen.dart';
import 'OrganizationStructureScreen.dart';
import '../../Notifiers/hrmProvider/hrmAccessiblePagesProvider.dart';
import 'oggchart.dart';
class HrmdashboardScreen extends StatefulWidget {
......@@ -17,17 +19,32 @@ class HrmdashboardScreen extends StatefulWidget {
}
class _HrmdashboardScreenState extends State<HrmdashboardScreen> {
final allowedPages = [
"Team Leave Request Approval",
"Team Attendance Approval",
"Leave Request List",
"Tour Bill List",
"Rewards List",
"Attendance Request List",
];
@override
void initState() {
super.initState();
Future.microtask(() =>
Provider.of<HrmAccessiblePagesProvider>(context, listen: false)
.fetchAccessiblePages(context));
}
@override
Widget build(BuildContext context) {
return Scaffold(
return SafeArea(
top: false,
child: Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
backgroundColor: const Color(0xFFCEEDFF),
// elevation: 2.0,
title: SizedBox(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
title: Row(
children: [
InkResponse(
onTap: () => Navigator.pop(context, true),
......@@ -37,28 +54,23 @@ class _HrmdashboardScreenState extends State<HrmdashboardScreen> {
),
),
const SizedBox(width: 10),
InkResponse(
onTap: () => Navigator.pop(context, true),
child: Text(
Text(
"HRM",
style: TextStyle(
fontSize: 18,
height: 1.1,
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w600,
color: AppColors.semi_black,
),
),
),
],
),
),
),
backgroundColor: Color(0xffF6F6F8),
backgroundColor: const Color(0xffF6F6F8),
body: SingleChildScrollView(
child: Column(
children: [
/// Background elements
/// Background
Stack(
children: [
Container(
......@@ -75,7 +87,7 @@ class _HrmdashboardScreenState extends State<HrmdashboardScreen> {
colors: [
Color(0xFFCEEDFF),
Color(0xFFf9f9fb),
Color(0xffF6F6F8)
Color(0xffF6F6F8),
],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
......@@ -93,25 +105,21 @@ class _HrmdashboardScreenState extends State<HrmdashboardScreen> {
),
),
/// Content
Column(
children: [
/// Top Section with Gradient
/// Top Illustration & Button
Container(
width: double.infinity,
padding: const EdgeInsets.only(top: 60, bottom: 30),
child: Column(
children: [
/// Illustration
SvgPicture.asset(
"assets/images/capa.svg",
height: 146,
width: 400,
fit: BoxFit.contain,
),
const SizedBox(height: 32),
/// Organization Structure Button
Container(
padding: const EdgeInsets.symmetric(
horizontal: 20, vertical: 8),
......@@ -154,174 +162,144 @@ class _HrmdashboardScreenState extends State<HrmdashboardScreen> {
],
),
),
/// Bottom Grid Section
// Bottom Grid Section
/// Grid Section
LayoutBuilder(
builder: (context, constraints) {
final itemWidth = 180.0; // Fixed desired width for each item
final availableWidth = constraints.maxWidth;
final crossAxisCount = (availableWidth / itemWidth).floor().clamp(2, 4);
return Padding(
padding: const EdgeInsets.all(14),
child: GridView.count(
crossAxisCount: crossAxisCount,
child: Consumer<HrmAccessiblePagesProvider>(
builder: (context, provider, child) {
if (provider.isLoading) {
return const Center(
child: CircularProgressIndicator());
}
if (provider.errorMessage != null) {
return Center(
child: Text(provider.errorMessage!));
}
final pages = (provider.response?.pagesAccessible ?? [])
.where((page) =>
allowedPages.contains(page.pageName))
.toList();
return GridView.builder(
itemCount: pages.length,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: (constraints.maxWidth / 180).floor().clamp(2, 4),
crossAxisSpacing: 8.5,
mainAxisSpacing: 16,
childAspectRatio: 1.7,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
children: [
_buildTile(
label: "Attendance List",
subtitle: "Real-time request",
assetIcon: "assets/svg/hrm/attendanceList.svg",
txtColor: const Color(0xff1487C9),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const Attendancelist(),
),
);
},
),
_buildTile(
label: "Leave Application",
subtitle: "Apply & Track",
assetIcon: "assets/svg/hrm/leaveApplication.svg",
itemBuilder: (context, index) {
final page = pages[index];
return _buildTile(
label: page.pageName ?? "",
subtitle: _getSubtitle(page.pageName ?? ""),
assetIcon: _getIcon(page.pageName ?? ""),
txtColor: const Color(0xff1487C9),
onTap: () {
Navigator.push(
onTap: () => _handleNavigation(
context,
MaterialPageRoute(
builder: (context) => const LeaveApplicationListScreen(),
page.pageName ?? "",
page.mode ?? "",
),
);
},
),
_buildTile(
label: "Rewards List",
subtitle: "Track earned rewards",
assetIcon: "assets/svg/hrm/rewardList.svg",
txtColor: const Color(0xff1487C9),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const RewardListScreen(),
),
);
},
),
_buildTile(
label: "Tour Expenses",
subtitle: "Submit and manage claims",
assetIcon: "assets/svg/hrm/tourExp.svg",
txtColor: const Color(0xff1487C9),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const TourExpensesListScreen(),
),
);
},
),
],
),
);
},
),
],
),
],
),
],
),
),
);
}
/// Reusable Tile Widget (Row style)
/// Reusable Tile Widget (Row style) - Updated to match design
/// Card builder
Widget _buildTile({
required String label,
required String subtitle,
required String assetIcon, // SVG/PNG asset
required String assetIcon,
required Color txtColor,
VoidCallback? onTap,
}) {
return LayoutBuilder(
builder: (context, constraints) {
return InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(14),
child: Container(
padding: EdgeInsets.symmetric(
vertical: 5,
horizontal: 15,
),
margin: EdgeInsets.symmetric(
vertical: 7,
horizontal: 5,
vertical: constraints.maxHeight * 0.05,
horizontal: constraints.maxWidth * 0.05,
),
margin: const EdgeInsets.symmetric(vertical: 7, horizontal: 5),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(14),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
/// Left side text
Expanded(
flex: 2,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
Flexible(
child: Text(
label,
style: TextStyle(
fontSize: 14,
color: AppColors.app_blue,
fontFamily: "JakartaMedium",
),
softWrap: true,
overflow: TextOverflow.visible,
),
SizedBox(height: 4),
Text(
),
const SizedBox(height: 4),
Flexible(
child: Text(
subtitle,
style: TextStyle(
fontSize: 12,
color: AppColors.grey_semi,
fontFamily: "JakartaMedium",
),
softWrap: true,
overflow: TextOverflow.visible,
),
),
],
),
),
SizedBox(width: 10),
/// Right side icon (SVG/PNG)
Expanded(
flex: 1,
child: Container(
height: 42,
width: 42,
height: constraints.maxHeight * 0.39,
width: constraints.maxHeight * 0.39,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: const Color(0xFFEDF8FF), // icon bg
color: const Color(0xFFEDF8FF),
),
child: Center(
child: SvgPicture.asset(
height: 25,
width: 25,
assetIcon,
fit: BoxFit.contain,
height: constraints.maxHeight * 0.19,
width: constraints.maxHeight * 0.19,
),
),
),
......@@ -330,5 +308,114 @@ class _HrmdashboardScreenState extends State<HrmdashboardScreen> {
),
),
);
},
);
}
/// Mapping subtitles
String _getSubtitle(String pageName) {
switch (pageName) {
case "Attendance Request List":
return "Real-time request";
case "Leave Request List":
return "Apply & Track";
case "Rewards List":
return "Track earned rewards";
case "Tour Bill List":
return "Submit and manage claims";
case "Team Leave Request Approval":
return "";
case "Team Attendance Approval":
return "";
default:
return "";
}
}
/// Mapping icons
String _getIcon(String pageName) {
switch (pageName) {
case "Attendance Request List":
return "assets/svg/hrm/attendanceList.svg";
case "Leave Request List":
return "assets/svg/hrm/leaveApplication.svg";
case "Rewards List":
return "assets/svg/hrm/rewardList.svg";
case "Tour Bill List":
return "assets/svg/hrm/tourExp.svg";
case "Team Leave Request Approval":
return "assets/svg/hrm/leaveApplication.svg";
case "Team Attendance Approval":
return "assets/svg/hrm/attendanceList.svg";
default:
return "assets/svg/hrm/groupIc.svg";
}
}
/// Navigation mapping
void _handleNavigation(
BuildContext context,
String pageName,
String mode,
) {
switch (pageName) {
case "Attendance Request List":
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AttendanceListScreen(mode: mode),
),
);
break;
case "Leave Request List":
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => LeaveApplicationListScreen(
mode: mode,
),
),
);
break;
case "Rewards List":
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => RewardListScreen(),
),
);
break;
case "Tour Bill List":
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => TourExpensesListScreen(),
),
);
break;
case "Team Leave Request Approval":
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => LeaveApplicationListScreen(
mode: mode,
),
),
);
break;
case "Team Attendance Approval":
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AttendanceListScreen(mode: mode),
),
);
break;
}
}
}
......@@ -11,7 +11,8 @@ import 'AddLeaveRequestScreen.dart';
import 'LeaveApplicationDetailScreen.dart';
class LeaveApplicationListScreen extends StatefulWidget {
const LeaveApplicationListScreen({super.key});
final mode;
const LeaveApplicationListScreen({super.key, required this.mode});
@override
State<LeaveApplicationListScreen> createState() => _LeaveApplicationListScreenState();
......@@ -30,11 +31,13 @@ class _LeaveApplicationListScreenState extends State<LeaveApplicationListScreen>
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
return SafeArea(
top: false,
child: ChangeNotifierProvider(
create: (_) {
final provider = LeaveApplicationListProvider();
Future.microtask(() {
provider.fetchLeaveApplications(context);
provider.fetchLeaveApplications(context, widget.mode);
});
return provider;
},
......@@ -59,6 +62,7 @@ class _LeaveApplicationListScreenState extends State<LeaveApplicationListScreen>
provider.setDateRangeFilter("Custom", customRange: dateRange);
provider.fetchLeaveApplications(
context,
widget.mode,
dateRange: "Custom",
customRange: dateRange,
);
......@@ -118,7 +122,7 @@ class _LeaveApplicationListScreenState extends State<LeaveApplicationListScreen>
final list = provider.response!.requestList!;
return ListView.builder(
padding: const EdgeInsets.all(8),
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 10),
itemCount: list.length,
itemBuilder: (context, index) {
final item = list[index];
......@@ -138,10 +142,11 @@ class _LeaveApplicationListScreenState extends State<LeaveApplicationListScreen>
MaterialPageRoute(
builder: (context) => LeaveApplicationDetailScreen(
leaveRequestId: item.id.toString(),
mode: widget.mode,
),
),
).then((_) {
provider.fetchLeaveApplications(context);
provider.fetchLeaveApplications(context,widget.mode);
});
},
......@@ -149,7 +154,7 @@ class _LeaveApplicationListScreenState extends State<LeaveApplicationListScreen>
margin: const EdgeInsets.symmetric(horizontal: 8.5, vertical: 5),
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12),
decoration: BoxDecoration(
color: Colors.white,
color: Colors.white,//Color(int.parse(item.rowColor!.replaceFirst('#', '0xff'))),
borderRadius: BorderRadius.circular(16),
),
child: Row(
......@@ -182,7 +187,9 @@ class _LeaveApplicationListScreenState extends State<LeaveApplicationListScreen>
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
item.leaveType ?? "-",
widget.mode == "teamleader"
? item.employeeName ?? "-"
: item.leaveType ?? "-",
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
......@@ -225,22 +232,23 @@ class _LeaveApplicationListScreenState extends State<LeaveApplicationListScreen>
),
),
// /// Right Status
// Container(
// padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
// decoration: BoxDecoration(
// color: _getStatusBackgroundColor(item.status),
// borderRadius: BorderRadius.circular(10),
// ),
// child: Text(
// item.status ?? "-",
// style: TextStyle(
// fontFamily: "JakartaMedium",
// fontSize: 13,
// color: _getStatusTextColor(item.status),
// ),
// ),
// ),
/// Right Status
if (widget.mode == "teamleader")
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: Color(0x00FFFFFF),
borderRadius: BorderRadius.circular(10),
),
child: Text(
item.leaveType ?? "-",
style: TextStyle(
fontFamily: "JakartaMedium",
fontSize: 13,
color: AppColors.app_blue,
),
),
),
],
),
),
......@@ -249,13 +257,17 @@ class _LeaveApplicationListScreenState extends State<LeaveApplicationListScreen>
);
},
),
)
),
SizedBox(height: 28,)
],
),
floatingActionButtonLocation:
FloatingActionButtonLocation.centerFloat,
floatingActionButton: InkResponse(
bottomNavigationBar: widget.mode == "teamleader"
? null
: Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
color: Colors.white,
child: InkResponse(
onTap: () {
HapticFeedback.selectionClick();
Navigator.push(
......@@ -267,7 +279,7 @@ class _LeaveApplicationListScreenState extends State<LeaveApplicationListScreen>
),
),
).then((_) {
provider.fetchLeaveApplications(context);
provider.fetchLeaveApplications(context, widget.mode);
});
// show add bill screen here
......@@ -275,8 +287,8 @@ class _LeaveApplicationListScreenState extends State<LeaveApplicationListScreen>
child: Container(
height: 45,
alignment: Alignment.center,
margin: EdgeInsets.symmetric(horizontal: 20),
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 5),
margin: EdgeInsets.symmetric(horizontal: 14),
padding: EdgeInsets.symmetric(horizontal: 6, vertical: 5),
decoration: BoxDecoration(
color: AppColors.app_blue,
borderRadius: BorderRadius.circular(15),
......@@ -291,15 +303,20 @@ class _LeaveApplicationListScreenState extends State<LeaveApplicationListScreen>
),
),
),
),
);
},
);
},
),
);
}
/// Get status background color
Color _getStatusBackgroundColor(String? status) {
switch (status?.toLowerCase()) {
......@@ -313,13 +330,14 @@ class _LeaveApplicationListScreenState extends State<LeaveApplicationListScreen>
}
}
/// Get status text color
Color _getStatusTextColor(String? status) {
switch (status?.toLowerCase()) {
case 'approved':
return AppColors.approved_text_color;
case 'rejected':
return AppColors.rejected_text_color;
return Colors.redAccent.shade200;
case 'requested':
default:
return AppColors.requested_text_color;
......
......@@ -17,7 +17,9 @@ class RewardListScreen extends StatefulWidget {
class _RewardListScreenState extends State<RewardListScreen> {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
return SafeArea(
top: false,
child: ChangeNotifierProvider(
create: (_) =>
RewardListProvider()..fetchRewardList(context),
child: Consumer<RewardListProvider>(
......@@ -296,6 +298,7 @@ class _RewardListScreenState extends State<RewardListScreen> {
);
}
)
),
);
}
......@@ -315,13 +318,13 @@ class _RewardListScreenState extends State<RewardListScreen> {
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.1),
blurRadius: 6,
offset: const Offset(0, 3),
)
],
// boxShadow: [
// BoxShadow(
// color: Colors.grey.withOpacity(0.1),
// blurRadius: 6,
// offset: const Offset(0, 3),
// )
// ],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
......
......@@ -19,7 +19,9 @@ class TourExpensesDetailsScreen extends StatefulWidget {
class _TourExpensesDetailsScreenState extends State<TourExpensesDetailsScreen>{
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
return SafeArea(
top: false,
child: ChangeNotifierProvider(
create: (_) => TourExpensesDetailsProvider()
..fetchTourExpensesDetails(context, widget.tourBillId),
child: Scaffold(
......@@ -179,8 +181,16 @@ class _TourExpensesDetailsScreenState extends State<TourExpensesDetailsScreen>{
context,
MaterialPageRoute(
builder:
(context) => Image.network(t.imageDirFilePath.toString()),
// Fileviewer(fileName: label, fileUrl: "assets/images/capa.svg"),
(
context,
) => Fileviewer(
fileName:
t.imageDirFilePath ??
"",
fileUrl:
t.imageDirFilePath ??
"",
),
),
);
},
......@@ -228,16 +238,19 @@ class _TourExpensesDetailsScreenState extends State<TourExpensesDetailsScreen>{
toDate: h.toDate ?? "-",
onViewTap: () {
debugPrint("Open: ${h.imageDirFilePath}");
showDialog(
context: context,
builder: (_) => Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(12),
child: Image.network(h.imageDirFilePath.toString())
Navigator.push(
context,
MaterialPageRoute(
builder:
(
context,
) => Fileviewer(
fileName:
h.imageDirFilePath ??
"",
fileUrl:
h.imageDirFilePath ??
"",
),
),
);
......@@ -285,16 +298,19 @@ class _TourExpensesDetailsScreenState extends State<TourExpensesDetailsScreen>{
date: o.otherDate ?? "-",
onViewTap: () {
debugPrint("Open: ${o.imageDirFilePath}");
showDialog(
context: context,
builder: (_) => Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(12),
child: Image.network(o.imageDirFilePath.toString())
Navigator.push(
context,
MaterialPageRoute(
builder:
(
context,
) => Fileviewer(
fileName:
o.imageDirFilePath ??
"",
fileUrl:
o.imageDirFilePath ??
"",
),
),
);
......@@ -314,6 +330,7 @@ class _TourExpensesDetailsScreenState extends State<TourExpensesDetailsScreen>{
},
),
),
),
);
}
......@@ -334,13 +351,13 @@ class _TourExpensesDetailsScreenState extends State<TourExpensesDetailsScreen>{
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.1),
blurRadius: 6,
offset: const Offset(0, 3),
)
],
// boxShadow: [
// BoxShadow(
// color: Colors.grey.withOpacity(0.1),
// blurRadius: 6,
// offset: const Offset(0, 3),
// )
// ],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
......@@ -407,13 +424,13 @@ class _TourExpensesDetailsScreenState extends State<TourExpensesDetailsScreen>{
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.1),
blurRadius: 6,
offset: const Offset(0, 3),
)
],
// boxShadow: [
// BoxShadow(
// color: Colors.grey.withOpacity(0.1),
// blurRadius: 6,
// offset: const Offset(0, 3),
// )
// ],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
......@@ -477,13 +494,13 @@ class _TourExpensesDetailsScreenState extends State<TourExpensesDetailsScreen>{
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.1),
blurRadius: 6,
offset: const Offset(0, 3),
)
],
// boxShadow: [
// BoxShadow(
// color: Colors.grey.withOpacity(0.1),
// blurRadius: 6,
// offset: const Offset(0, 3),
// )
// ],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
......@@ -548,13 +565,13 @@ class _TourExpensesDetailsScreenState extends State<TourExpensesDetailsScreen>{
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.1),
blurRadius: 6,
offset: const Offset(0, 3),
)
],
// boxShadow: [
// BoxShadow(
// color: Colors.grey.withOpacity(0.1),
// blurRadius: 6,
// offset: const Offset(0, 3),
// )
// ],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
......@@ -681,7 +698,7 @@ class _TourExpensesDetailsScreenState extends State<TourExpensesDetailsScreen>{
bottomRight: Radius.circular(30),
),
),
elevation: 2,
elevation: 0,
child: Container(
decoration: const BoxDecoration(
color: Colors.white,
......
......@@ -168,38 +168,36 @@ class _TourExpensesListScreenState extends State<TourExpensesListScreen> {
],
),
floatingActionButtonLocation:
FloatingActionButtonLocation.centerFloat,
floatingActionButton: InkResponse(
onTap: () {
bottomNavigationBar: Container(
padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 10),
color: Colors.white,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xff1487c9), // App blue
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
),
padding: const EdgeInsets.symmetric(vertical: 14),
elevation: 0, // Optional: remove shadow
),
onPressed: () {
HapticFeedback.selectionClick();
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
const AddBillScreen(pageTitleName: "Add Bill",),
settings: const RouteSettings(
name: 'AddTourExpBillScreen'),
builder: (context) => const AddBillScreen(pageTitleName: "Add Bill"),
settings: const RouteSettings(name: 'AddTourExpBillScreen'),
),
).then((_) {
provider.fetchTourExpenses(context, "1");
});
// show add bill screen here
},
child: Container(
height: 45,
alignment: Alignment.center,
margin: EdgeInsets.symmetric(horizontal: 20),
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 5),
decoration: BoxDecoration(
color: AppColors.app_blue,
borderRadius: BorderRadius.circular(15),
),
child: Text(
child: const Text(
"Add Bill",
style: TextStyle(
fontSize: 15,
fontSize: 16,
fontFamily: "JakartaMedium",
fontWeight: FontWeight.w500,
color: Colors.white,
),
),
......
......@@ -57,6 +57,7 @@ export 'package:generp/Notifiers/crmProvider/followUpUpdateProvider.dart';
export 'package:generp/Notifiers/crmProvider/appointmentCalendarProvider.dart';
export 'package:generp/Notifiers/crmProvider/addNewLeadsandProspectsProvider.dart';
export 'package:generp/Notifiers/hrmProvider/hrmAccessiblePagesProvider.dart';
export 'package:generp/Notifiers/hrmProvider/attendanceListProvider.dart';
export 'package:generp/Notifiers/hrmProvider/AttendanceDetailsProvider.dart';
export 'package:generp/Notifiers/hrmProvider/tourExpensesProvider.dart';
......
......@@ -4995,11 +4995,13 @@ class ApiCalling {
type,
from,
to,
mode
) async {
try {
Map<String, String> data = {
'emp_id': (empId).toString(),
'session_id': (session).toString(),
'mode': (mode),
'type': (type),
'from': (from),
'to': (to),
......@@ -5019,6 +5021,7 @@ class ApiCalling {
}
}
static Future<attendanceRequestDetailsResponse?> attendanceRequestDetailAPI(
empId,
session,
......@@ -5045,6 +5048,38 @@ class ApiCalling {
}
}
static Future<CommonResponse?> attendanceRequestApproveRejectAPI(
session,
empId,
mode,
type,
remarks,
id,
) async {
try {
Map<String, String> data = {
'session_id': (session).toString(),
'emp_id': (empId).toString(),
'mode': (mode).toString(),
'type': (type).toString(),
'remarks': (remarks).toString(),
'id': (id).toString(),
};
final res = await post(data, AttendanceRequestRejectUrl, {});
if (res != null) {
print("Attendance App Reje:${data}");
debugPrint(res.body);
return CommonResponse.fromJson(jsonDecode(res.body));
} else {
debugPrint("Null Response");
return null;
}
} catch (e) {
debugPrint('hello bev=bug $e ');
return null;
}
}
static Future<CommonResponse?> addAttendanceRequestAPI({
required String sessionId,
required String empId,
......@@ -5317,12 +5352,14 @@ class ApiCalling {
// Leave Application api
// Leave Application api
static Future<leaveApplicationLIstResponse?> leaveApplicationListAPI(
session,
empId,
dateFrom,
dateTo
dateTo,
mode
) async {
try {
Map<String, String> data = {
......@@ -5330,6 +5367,8 @@ class ApiCalling {
'emp_id': (empId).toString(),
'requested_date_from': (dateFrom),
'requested_date_to': (dateTo),
'mode': (mode),
};
final res = await post(data, LeaveApplicationListUrl, {});
if (res != null) {
......@@ -5408,6 +5447,38 @@ class ApiCalling {
}
}
static Future<CommonResponse?> leaveRequestRejectApproveAPI(
session,
empId,
mode,
type,
remarks,
id,
) async {
try {
Map<String, String> data = {
'session_id': (session).toString(),
'emp_id': (empId).toString(),
'mode': (mode).toString(),
'type': (type).toString(),
'remarks': (remarks).toString(),
'id': (id).toString(),
};
final res = await post(data, LeaveRequestRejectAprroveUrl, {});
if (res != null) {
print(data);
debugPrint(res.body);
return CommonResponse.fromJson(jsonDecode(res.body));
} else {
debugPrint("Null Response");
return null;
}
} catch (e) {
debugPrint('hello bev=bug $e ');
return null;
}
}
// static Future<CommonResponse?> TpcIssueListApprovalAPI(
// empId,
......
......@@ -180,13 +180,16 @@ const crmDashboardQuotationsUrl = "${baseUrl_test}crm_dashboard_quotations_list"
const ogcharturl = "${baseUrl_test}organisation_structures";
const JobDesciptionUrl ="${baseUrl_test}job_description";
///HRM
//Attendance
const HrmAccessiblePagesUrl ="${baseUrl_test}hrm_accessible_pages";
const AttendanceRequestListUrl ="${baseUrl_test}attendance_request_list";
const AttendanceRequestDetailsUrl ="${baseUrl_test}attendance_request_details";
const AddAttendanceRequestUrl ="${baseUrl_test}add_attendance_request";
const AttendanceRequestRejectUrl ="${baseUrl_test}attendance_approve_reject";
const AttendanceRequestAproveUrl ="${baseUrl_test}attendance_approve_reject";
// reward list
const RewardListUrl ="${baseUrl_test}hrm_emp_self_rewards";
// Tour Expenses hrm_emp_self_rewards
......@@ -198,6 +201,7 @@ const AddTourExpensesUrl ="${baseUrl_test}add_tour_bill";
const LeaveApplicationListUrl ="${baseUrl_test}leave_request_list";
const LeaveApplicationDetailsUrl ="${baseUrl_test}leave_request_details";
const LeaveRequestAdditionUrl ="${baseUrl_test}add_leave_request";
const LeaveRequestRejectAprroveUrl ="${baseUrl_test}leaves_approve_reject";
......