<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="purple_200">#FFBB86FC</color>
<color name="purple_500">#FF6200EE</color>
<color name="purple_700">#FF3700B3</color>
<color name="teal_200">#FF03DAC5</color>
<color name="teal_700">#FF018786</color>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
<color name="app_color">#011842</color>
<color name="editBackgroundColor">#E4EFF9</color>
<color name="blue">#0000FF</color>
<color name="transparent">#CCFFFFFF</color>
<color name="grey">#696969</color>\
<color name="red">#EC454C</color>
<color name="brown">#663300</color>
<color name="green">#27ae60</color>
<color name="app_color_opacity">#26011842</color>
</resources>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#FFFFFF</color>
</resources>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="purple_200">#FFBB86FC</color>
<color name="purple_500">#FF6200EE</color>
<color name="purple_700">#FF3700B3</color>
<color name="teal_200">#FF03DAC5</color>
<color name="teal_700">#FF018786</color>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
<color name="app_color">#011842</color>
<color name="editBackgroundColor">#E4EFF9</color>
<color name="blue">#0000FF</color>
<color name="transparent">#CCFFFFFF</color>
<color name="grey">#696969</color>\
<color name="red">#EC454C</color>
<color name="brown">#663300</color>
<color name="green">#27ae60</color>
<color name="app_color_opacity">#26011842</color>
</resources>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#FFFFFF</color>
</resources>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external_files" path="images/"/>
</paths>
......@@ -42,6 +42,7 @@ class RequestList {
String? checkOutTime;
String? status;
String? requestedDatetime;
String? employeeName;
RequestList(
{this.id,
......@@ -53,7 +54,9 @@ class RequestList {
this.chechOutType,
this.checkOutTime,
this.status,
this.requestedDatetime});
this.requestedDatetime,
this.employeeName,
});
RequestList.fromJson(Map<String, dynamic> json) {
id = json['id'];
......@@ -66,6 +69,7 @@ class RequestList {
checkOutTime = json['check_out_time'];
status = json['status'];
requestedDatetime = json['requested_datetime'];
employeeName = json['employee_name'];
}
Map<String, dynamic> toJson() {
......@@ -80,6 +84,7 @@ class RequestList {
data['check_out_time'] = this.checkOutTime;
data['status'] = this.status;
data['requested_datetime'] = this.requestedDatetime;
data['employee_name'] = this.employeeName;
return data;
}
}
......@@ -38,6 +38,8 @@ class RequestList {
String? toPeriod;
String? status;
String? leaveType;
String? rowColor;
String? employeeName;
RequestList(
......@@ -50,6 +52,8 @@ class RequestList {
toPeriod = json['to_period'];
status = json['status'];
leaveType = json["leave_type"];
rowColor = json["row_colur"];
employeeName = json["employee_name"];
}
Map<String, dynamic> toJson() {
......@@ -60,6 +64,8 @@ class RequestList {
data['to_period'] = this.toPeriod;
data['status'] = this.status;
data["leave_type"] = this.leaveType;
data["row_colur"] = this.rowColor;
data["employee_name"] = this.employeeName;
return data;
}
}
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../../Models/hrmModels/leaveApplicationDetailsResponse.dart';
import '../../Models/ordersModels/commonResponse.dart';
import '../../services/api_calling.dart';
import '../HomeScreenNotifier.dart';
......@@ -9,6 +11,11 @@ class LeaveApplicationDetailsProvider extends ChangeNotifier {
leaveApplicationDetailsResponse? _response;
bool _isLoading = false;
String? _errorMessage;
bool _isSubmitting = false;
bool get isSubmitting => _isSubmitting;
CommonResponse? _StatusResponse;
CommonResponse? get Response => _StatusResponse;
leaveApplicationDetailsResponse? get response => _response;
bool get isLoading => _isLoading;
......@@ -43,6 +50,60 @@ class LeaveApplicationDetailsProvider extends ChangeNotifier {
notifyListeners();
}
Future<void> leaveRequestRejectApprove(
BuildContext context, {
required String mode,
required String type,
required String remarks,
required String id,
}) async {
_isSubmitting = true;
_errorMessage = null;
_StatusResponse = null;
notifyListeners();
try {
final homeProvider = Provider.of<HomescreenNotifier>(context, listen: false);
final result = await ApiCalling.leaveRequestRejectApproveAPI(
homeProvider.session,
homeProvider.empId,
mode,
type,
remarks,
id,
);
if (result != null) {
_StatusResponse = result;
if (result.error != null && result.error!.isNotEmpty) {
_errorMessage = result.error;
} else {
// Show success snack bar
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
"Leave request ${type.toLowerCase()} successfully.",
),
backgroundColor: Colors.green,
behavior: SnackBarBehavior.floating,
),
);
}
} else {
_errorMessage = "Failed to $type leave request!";
}
} catch (e) {
_errorMessage = "Error while processing leave request: $e";
}
_isSubmitting = false;
notifyListeners();
}
/// Clear the current response data
void clearData() {
_response = null;
......
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:generp/Models/hrmModels/attendanceRequestDetailsResponse.dart';
import 'package:provider/provider.dart';
import '../../Models/hrmModels/attendanceRequestListResponse.dart';
import '../../Models/ordersModels/commonResponse.dart';
import '../../services/api_calling.dart';
import '../HomeScreenNotifier.dart';
......@@ -12,6 +14,11 @@ class AttendanceDetailsProvider extends ChangeNotifier {
attendanceRequestDetailsResponse? _response;
bool _isLoading = false;
String? _errorMessage;
bool _isSubmitting = false;
bool get isSubmitting => _isSubmitting;
CommonResponse? _RejectResponse;
CommonResponse? get addResponse => _RejectResponse;
attendanceRequestDetailsResponse? get response => _response;
bool get isLoading => _isLoading;
......@@ -39,4 +46,51 @@ class AttendanceDetailsProvider extends ChangeNotifier {
_isLoading = false;
notifyListeners();
}
Future<void> rejectApproveAttendanceRequest(
BuildContext context, {
required String mode,
required String type,
required String remarks,
required String id,
}) async {
_isSubmitting = true;
_errorMessage = null;
_RejectResponse = null;
notifyListeners();
try {
final homeProvider = Provider.of<HomescreenNotifier>(context, listen: false);
print("############################+++++++++++++++++##########");
final result = await ApiCalling.attendanceRequestApproveRejectAPI(
homeProvider.session,
homeProvider.empId,
mode,
type,
remarks,
id,
);
if (result != null) {
_RejectResponse = result;
if (result.error != null && result.error!.isNotEmpty) {
_errorMessage = result.error;
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text("Attendance request rejected successfully."),
),
);
}
} else {
_errorMessage = "Failed to reject attendance request!";
}
} catch (e) {
_errorMessage = "Error rejecting attendance request: $e";
}
_isSubmitting = false;
notifyListeners();
}
}
......@@ -123,10 +123,12 @@ class Attendancelistprovider extends ChangeNotifier {
return null; // everything ok
}
CommonResponse? _RejectResponse;
CommonResponse? get RejectResponse => _RejectResponse;
/// Fetch attendance request list with filters
Future<void> fetchAttendanceRequests(BuildContext context,
Future<void> fetchAttendanceRequests(BuildContext context, mode,
{String? type, String? dateRange, DateTimeRange? customRange}) async {
_isLoading = true;
_errorMessage = null;
......@@ -152,6 +154,7 @@ class Attendancelistprovider extends ChangeNotifier {
apiType,
dateParams['from']!,
dateParams['to']!,
mode
);
debugPrint('Fetching attendance from: ${dateParams['from']} to: ${dateParams['to']}');
......@@ -230,8 +233,52 @@ class Attendancelistprovider extends ChangeNotifier {
notifyListeners();
}
Future<void> rejectApproveAttendanceRequest({
required String session,
required String empId,
required String mode,
required String type,
required String remarks,
required String id,
}) async {
_isSubmitting = true;
_errorMessage = null;
_RejectResponse = null;
notifyListeners();
try {
final result = await ApiCalling.attendanceRequestApproveRejectAPI(
session,
empId,
mode,
type,
remarks,
id,
);
print("*********************************object");
if (result != null) {
_RejectResponse = result;
if (result.error != null && result.error!.isNotEmpty) {
_errorMessage = result.error;
} else {
debugPrint("Attendance request $type successfully.");
}
} else {
_errorMessage = "Failed to process attendance request!";
}
} catch (e) {
_errorMessage = "Error processing attendance request: $e";
}
_isSubmitting = false;
notifyListeners();
}
/// Apply filters coming from bottom sheet
void updateFiltersFromSheet(
mode,
BuildContext context, {
required String type,
required String selectedValue,
......@@ -243,6 +290,7 @@ class Attendancelistprovider extends ChangeNotifier {
fetchAttendanceRequests(
context,
mode,
type: _selectedType,
dateRange: _selectedDateRange,
customRange: _customDateRange,
......@@ -250,13 +298,13 @@ class Attendancelistprovider extends ChangeNotifier {
}
/// Set type filter and refresh data
void setTypeFilter(BuildContext context, String type) {
void setTypeFilter(BuildContext context, String type, mode) {
_selectedType = type;
fetchAttendanceRequests(context);
fetchAttendanceRequests(context, mode);
}
/// Set date range filter and refresh data
void setDateRangeFilter(BuildContext context, String dateRange,
void setDateRangeFilter(BuildContext context, String dateRange, mode,
{DateTimeRange? customRange}) {
_selectedDateRange = dateRange;
if (customRange != null) {
......@@ -264,24 +312,24 @@ class Attendancelistprovider extends ChangeNotifier {
fromDateController.text = _formatDate(customRange.start);
toDateController.text = _formatDate(customRange.end);
}
fetchAttendanceRequests(context);
fetchAttendanceRequests(context, mode);
}
/// Clear all filters and refresh data
void clearFilters(BuildContext context) {
void clearFilters(BuildContext context, mode) {
_selectedType = "All";
_selectedDateRange = "This Month";
_customDateRange = null;
fromDateController.clear();
toDateController.clear();
fetchAttendanceRequests(context);
fetchAttendanceRequests(context, mode);
}
/// Reset form and data
void resetForm(BuildContext context) {
void resetForm(BuildContext context, mode) {
_response = null;
_errorMessage = null;
clearFilters(context);
clearFilters(context, mode);
notifyListeners();
}
......@@ -343,9 +391,10 @@ class Attendancelistprovider extends ChangeNotifier {
}
/// Apply filters and refresh data
void applyFilters(BuildContext context) {
void applyFilters(BuildContext context, mode) {
fetchAttendanceRequests(
context,
mode,
type: _selectedType,
dateRange: _selectedDateRange,
customRange: _customDateRange,
......
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../../Models/hrmModels/hrmAccessiblePagesResponse.dart';
import '../../services/api_calling.dart';
import '../HomeScreenNotifier.dart';
class HrmAccessiblePagesProvider extends ChangeNotifier {
hrmAccessiblePagesResponse? _response;
bool _isLoading = false;
String? _errorMessage;
hrmAccessiblePagesResponse? get response => _response;
bool get isLoading => _isLoading;
String? get errorMessage => _errorMessage;
/// Fetch HRM Accessible Pages
Future<void> fetchAccessiblePages(BuildContext context) async {
_isLoading = true;
_errorMessage = null;
_response = null;
notifyListeners();
try {
final provider = Provider.of<HomescreenNotifier>(context, listen: false);
final result = await ApiCalling.hrmAccessiblePagesAPI(
provider.empId,
provider.session,
);
if (result != null) {
_response = result;
if (_response?.pagesAccessible == null || _response!.pagesAccessible!.isEmpty) {
_errorMessage = "No accessible pages found!";
}
} else {
_errorMessage = "No data found!";
}
} catch (e) {
_errorMessage = "Something went wrong: $e";
debugPrint("Error fetching HRM accessible pages: $e");
}
_isLoading = false;
notifyListeners();
}
/// Clear stored data
void clearData() {
_response = null;
_errorMessage = null;
notifyListeners();
}
}
......@@ -63,7 +63,7 @@ class LeaveApplicationListProvider extends ChangeNotifier {
DateTime? get selectedDate => _selectedDate;
/// Fetch leave application list with filters
Future<void> fetchLeaveApplications(BuildContext context,
Future<void> fetchLeaveApplications(BuildContext context, mode,
{String? status, String? dateRange, DateTimeRange? customRange}) async {
_isLoading = true;
_errorMessage = null;
......@@ -83,6 +83,7 @@ class LeaveApplicationListProvider extends ChangeNotifier {
provider.empId,
dateParams['from']!,
dateParams['to']!,
mode
);
debugPrint(
......@@ -253,9 +254,9 @@ class LeaveApplicationListProvider extends ChangeNotifier {
}
/// Show Cupertino DatePicker for leave form
void showDatePickerDialog(BuildContext context,
{bool isFromDate = true}) {
DateTime? currentDate = DateTime.now();
void showDatePickerDialog(BuildContext context, {bool isFromDate = true}) {
DateTime now = DateTime.now();
DateTime? currentDate = now;
showCupertinoModalPopup<void>(
context: context,
......@@ -286,10 +287,10 @@ class LeaveApplicationListProvider extends ChangeNotifier {
onPressed: () {
if (isFromDate) {
fromDateField.text =
_formatDate(currentDate ?? DateTime.now());
_formatDate(currentDate ?? now);
} else {
toDateField.text =
_formatDate(currentDate ?? DateTime.now());
_formatDate(currentDate ?? now);
}
Navigator.pop(context);
},
......@@ -300,7 +301,9 @@ class LeaveApplicationListProvider extends ChangeNotifier {
Expanded(
child: CupertinoDatePicker(
dateOrder: DatePickerDateOrder.dmy,
initialDateTime: currentDate,
initialDateTime: now,
minimumDate: DateTime(now.year, now.month, now.day),
maximumDate: DateTime(now.year + 5), // limit
mode: CupertinoDatePickerMode.date,
onDateTimeChanged: (DateTime newDate) {
currentDate = newDate;
......@@ -315,9 +318,10 @@ class LeaveApplicationListProvider extends ChangeNotifier {
}
/// Apply filters
void applyFilters(BuildContext context) {
void applyFilters(BuildContext context,mode) {
fetchLeaveApplications(
context,
mode,
status: _selectedStatus,
dateRange: _selectedDateRange,
customRange: _customDateRange,
......
......@@ -227,6 +227,7 @@ class MyApp extends StatelessWidget {
ChangeNotifierProvider(create: (_) => followUpUpdateProvider()),
ChangeNotifierProvider(create: (_) => Appointmentcalendarprovider()),
ChangeNotifierProvider(create: (_) => Addnewleadsandprospectsprovider()),
ChangeNotifierProvider(create: (_) => HrmAccessiblePagesProvider()),
ChangeNotifierProvider(create: (_) => Attendancelistprovider()),
ChangeNotifierProvider(create: (_) => AttendanceDetailsProvider()),
ChangeNotifierProvider(create: (_) => TourExpensesProvider()),
......
import 'package:dropdown_button2/dropdown_button2.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:intl/intl.dart';
import '../Utils/app_colors.dart';
import '../Utils/dropdownTheme.dart';
......@@ -351,6 +352,8 @@ class CommonFilter2 {
],
),
),
if (tempSelectedValue == 'Custom') ...[
const SizedBox(height: 16),
Container(
......@@ -365,12 +368,13 @@ class CommonFilter2 {
Padding(
padding: const EdgeInsets.only(top: 12.0),
child: Text(
'Selected: ${formatDate(tempSelectedDateRange!.start)} to ${formatDate(tempSelectedDateRange!.end)}',
'Selected: ${DateFormat("dd MMM yyyy").format(tempSelectedDateRange!.start)} to ${DateFormat("dd MMM yyyy").format(tempSelectedDateRange!.end)}',
style: TextStyle(fontSize: 14, color: Colors.grey[600]),
),
),
],
const SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.end,
......
......@@ -1863,21 +1863,43 @@ class _MyHomePageState extends State<MyHomePage> {
profile.employeeeID,
profile.mobileNUmber,
];
final itemText = textHeadings[index]?.toString() ?? "-";
return SizedBox(
height: 40,
child: Align(
alignment: Alignment.centerLeft,
child: InkWell(
onTap: () {
showJobDescriptionSheet(context, [
"Statewise End to end sales activities reg booking and dispatches and payment collection and branch visit every month & quarterly basis.",
"Conducting monthly/Quarterly/Annually– sales meeting, review of targets and achievements of total team.",
"Team CRM Tracking, Order Update Track and as well as payment entry in CRM by Team.",
"If required special Price to be taken from Prasad, Madhavi Madam/MD Sir.",
"Preparation of MIS reports on monthly basis (Rating wise data, employee wise data, TIV etc.).",
"Dispatch co-ordination with factory team- Anuradha / Sai Ram (commercial clearance with Susmitha / Rajeevi).",
"Commercial / Technical Support to BDE team order finalisation. If required client visit.",
"Team tour bills approvals in CRM.",
"Level -1 approvals to be given to sales team orders.",
"Outstanding payment collection followed on regular basis.",
]);
},
// no click for others
child: Text(
"${textHeadings[index].toString()}",
itemText,
textAlign: TextAlign.left,
style: TextStyle(
fontSize: 14,
color: AppColors.semi_black,
color: index == 2 ? AppColors.semi_black : AppColors.semi_black, // highlight clickable
decoration: index == 2 ? TextDecoration.underline : null,
),
),
),
),
);
},
),
),
],
......@@ -1943,6 +1965,116 @@ class _MyHomePageState extends State<MyHomePage> {
);
}
Future<void> showJobDescriptionSheet(
BuildContext context,
List<String> jobPoints,
) {
return showModalBottomSheet(
useSafeArea: true,
isDismissible: true,
isScrollControlled: true,
showDragHandle: true,
enableDrag: true,
backgroundColor: Colors.white,
context: context,
builder: (context) {
return SafeArea(
child: Container(
margin: const EdgeInsets.only(
bottom: 15,
left: 15,
right: 15,
top: 30,
),
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
/// Heading
Text(
"Job Description",
style: TextStyle(
fontFamily: "JakartaMedium",
fontSize: 16,
color: AppColors.app_blue, // same as Logout "Yes, Logout" button
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 15),
/// Bullet points list
...jobPoints.map(
(point) => Padding(
padding: const EdgeInsets.symmetric(vertical: 6),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"• ",
style: TextStyle(
fontSize: 14,
color: AppColors.semi_black,
),
),
Expanded(
child: Text(
point,
style: TextStyle(
fontSize: 14,
color: AppColors.semi_black,
fontFamily: "JakartaRegular",
height: 1.4, // line spacing
),
),
),
],
),
),
),
const SizedBox(height: 20),
/// Close button
InkWell(
onTap: () => Navigator.pop(context),
child: Container(
alignment: Alignment.center,
height: 45,
margin: const EdgeInsets.symmetric(
horizontal: 5.0,
vertical: 5.0,
),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15.0),
),
child: Center(
child: Text(
"Close",
textAlign: TextAlign.center,
style: TextStyle(
color: AppColors.app_blue,
fontFamily: "JakartaMedium",
fontSize: 15,
),
),
),
),
),
],
),
),
),
);
},
);
}
Future<void> _showLogoutBottomSheet(BuildContext context) {
return showModalBottomSheet(
useSafeArea: true,
......
......@@ -125,7 +125,9 @@ class _AddLeaveRequestState extends State<AddLeaveRequest> {
}
Widget _scaffold(BuildContext context, LeaveApplicationListProvider provider) {
return Scaffold(
return SafeArea(
top: false,
child: Scaffold(
resizeToAvoidBottomInset: true,
backgroundColor: AppColors.scaffold_bg_color,
appBar: appbarNew(context, widget.pageTitleName, 0xFFFFFFFF),
......@@ -292,8 +294,8 @@ class _AddLeaveRequestState extends State<AddLeaveRequest> {
child: Container(
height: 45,
alignment: Alignment.center,
margin: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
margin: const EdgeInsets.symmetric(horizontal: 18, vertical: 10),
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8),
decoration: BoxDecoration(
color: AppColors.app_blue,
borderRadius: BorderRadius.circular(15),
......@@ -309,6 +311,7 @@ class _AddLeaveRequestState extends State<AddLeaveRequest> {
),
),
),
),
);
}
......