import 'package:flutter/material.dart';
class OrganizationStructureScreen extends StatelessWidget {
final List<Department> departments = [
Department(
name: "Engineering",
teams: [
Team(name: "Mobile Team", members: ["Mohit", "Srinivas", ]),
Team(name: "Backend Team", members: ["Dheeraj", "Satya","Sneha"]),
],
),
Department(
name: "Sales & Marketing",
teams: [
Team(name: "Digital Marketing", members: ["Kiran", "Priya"]),
Team(name: "Field Sales", members: ["Raj", "Anjali"]),
],
),
Department(
name: "HR & Admin",
teams: [
Team(name: "Recruitment", members: ["Naresh"]),
Team(name: "Operations", members: ["Suresh", "Divya"]),
],
),
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("Organization Structure")),
body: ListView.builder(
padding: const EdgeInsets.all(16),
itemCount: departments.length,
itemBuilder: (context, deptIndex) {
final dept = departments[deptIndex];
return Card(
margin: const EdgeInsets.only(bottom: 16),
elevation: 2,
child: ExpansionTile(
title: Text(
"${dept.name} not ready",
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
children: dept.teams.map((team) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
team.name,
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
),
const SizedBox(height: 4),
Wrap(
spacing: 8,
children: team.members.map((member) {
return Chip(
label: Text(member),
backgroundColor: Colors.blue.shade50,
);
}).toList(),
),
const SizedBox(height: 8),
],
),
);
}).toList(),
),
);
},
),
);
}
}
class Department {
final String name;
final List<Team> teams;
Department({required this.name, required this.teams});
}
class Team {
final String name;
final List<String> members;
Team({required this.name, required this.members});
}
import 'package:dotted_line/dotted_line.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:generp/screens/hrm/RewardSearchScreen.dart';
import 'package:provider/provider.dart';
import '../../Notifiers/hrmProvider/rewardListProvider.dart';
import '../../Utils/app_colors.dart';
class RewardListScreen extends StatefulWidget {
const RewardListScreen({Key? key}) : super(key: key);
@override
State<RewardListScreen> createState() => _RewardListScreenState();
}
class _RewardListScreenState extends State<RewardListScreen> {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (_) =>
RewardListProvider()..fetchRewardList(context),
child: Consumer<RewardListProvider>(
builder: (context, provider, child) {
return Scaffold(
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: 25,
),
),
const SizedBox(width: 10),
Text(
"Reward List",
style: TextStyle(
fontSize: 18,
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w600,
color: AppColors.semi_black,
),
),
],
),
// actions: [
// InkResponse(
// onTap: () {
// Navigator.push(
// context,
// MaterialPageRoute(
// builder: (context) => RewardSearchScreen(),
// settings: const RouteSettings(
// name: 'AddLiveAttendanceScreen',
// ),
// ),
// ).then((_) {
// });
// },
// child: SvgPicture.asset(
// "assets/svg/search_ic.svg",
// height: 25,
// ),
// ),
// const SizedBox(width: 20),
// ],
),
backgroundColor: Color(0xFFF6F6F8),
body: Builder(
builder: (context) {
if (provider.isLoading) {
return const Center(child: CircularProgressIndicator(
color: Colors.blue,));
}
if (provider.errorMessage != null) {
return Center(child: Text(provider.errorMessage!));
}
if (provider.response == null) {
return const Center(child: Text("No details found"));
}
final rewardDetail = provider.response!;
final rewardResponse = provider.response!;
final rewards = rewardResponse.rewardsList; // main list object
final achieved = rewardResponse.achievedAmount ?? "0";
final disbursed = rewardResponse.disbursedAmount ?? "0";
final balance = rewardResponse.balanceAmount ?? "0";
return SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
children: [
/// --- Top Summary Cards ---
Stack(
children: [
Container(
height: 110,
width: double.infinity,
padding: const EdgeInsets.all(18),
decoration: BoxDecoration(
color: const Color(0xffd9ffd6),
borderRadius: BorderRadius.circular(18),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"₹${achieved}", // Achieved Amount from response
style: const TextStyle(
fontSize: 20,
color: Color(0xff0D9C00),
fontStyle: FontStyle.normal,
fontWeight: FontWeight.w500,
),
),
const SizedBox(height: 10),
const Text(
"Achievement Amount",
style: TextStyle(
fontSize: 14,
color: Color(0xff2D2D2D),
fontStyle: FontStyle.normal,
fontWeight: FontWeight.w400,
),
),
],
),
),
// Positioned SVG Icon
Positioned(
bottom: 8,
right: 12,
child: Container(
height: 42,
width: 42,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: const Color(0xA0FFFFFF), // icon bg
),
child: Center(
child: SvgPicture.asset(
height: 25,
width: 25,
"assets/svg/hrm/achievement_ic.svg",
fit: BoxFit.contain,
),
),
),
),
],
),
const SizedBox(height: 12),
Row(
children: [
Expanded(
child: Container(
height: 110,
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: const Color(0xffe8ddff),
borderRadius: BorderRadius.circular(16),
),
child: Stack(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"₹${disbursed}", // Disbursed Amount
style: const TextStyle(
fontSize: 20,
color: Color(0xff493272),
fontWeight: FontWeight.w500,
),
),
const SizedBox(height: 8),
const Text(
"Disbursed \nAmount",
style: TextStyle(
fontSize: 14,
color: Color(0xff2D2D2D),
fontWeight: FontWeight.w400,
),
),
],
),
Positioned(
bottom: 2,
right: 2,
child: Container(
height: 42,
width: 42,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: const Color(0xA0FFFFFF), // icon bg
),
child: Center(
child: SvgPicture.asset(
height: 25,
width: 25,
"assets/svg/hrm/location_ic.svg",
fit: BoxFit.contain,
),
),
),
),
],
),
),
),
const SizedBox(width: 12),
Expanded(
child: Container(
height: 110,
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: const Color(0xfffffbc3),
borderRadius: BorderRadius.circular(16),
),
child: Stack(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"₹${balance}", // Balance Amount
style: const TextStyle(
fontSize: 18,
color: Color(0xff605C00),
),
),
const SizedBox(height: 8),
const Text(
"Balance \nAmount",
style: TextStyle(
fontSize: 14,
color: Color(0xff2D2D2D),
fontWeight: FontWeight.w400,
),
),
],
),
Positioned(
bottom: 2,
right: 2,
child: Container(
height: 42,
width: 42,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: const Color(0xA0FFFFFF), // icon bg
),
child: Center(
child: SvgPicture.asset(
height: 25,
width: 25,
"assets/svg/hrm/ballance_ic.svg",
fit: BoxFit.contain,
),
),
),
),
],
),
),
),
],
),
const SizedBox(height: 20),
/// --- Reward List Card ---
if (rewards != null)
_rewardListCard(
title: rewards.description ?? "-", // rewardsList fields
dateTime: rewards.dateTime ?? "-",
achieved: achieved,
disbursed: disbursed,
balance: balance,
enteredBy: rewards.enteredBy ?? "-",
)
else
const Text("No rewards available"),
],
),
);
}
),
);
}
)
);
}
/// Reusable Reward Card Function
Widget _rewardListCard({
required String title,
required String dateTime,
required String achieved,
required String disbursed,
required String balance,
required String enteredBy,
}) {
return Container(
margin: const EdgeInsets.only(bottom: 16),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.1),
blurRadius: 6,
offset: const Offset(0, 3),
)
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
/// Header Row
Row(
children: [
CircleAvatar(
radius: 22.5,
backgroundColor: Color(0xffEDF8FF),
child: SvgPicture.asset(
height: 28,
width: 28,
"assets/svg/hrm/rewardList.svg",
fit: BoxFit.contain,
),
),
const SizedBox(width: 8),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(
fontSize: 14.5,
color: Color(0xff2D2D2D),
fontFamily: "Plus Jakarta Sans",
fontStyle: FontStyle.normal,
fontWeight: FontWeight.w400),
),
Text(
dateTime,
style: const TextStyle(
fontSize: 12.5,
color: Color(0xff818181),
fontFamily: "Plus Jakarta Sans",
fontStyle: FontStyle.normal,
fontWeight: FontWeight.w400),
),
],
),
),
],
),
const SizedBox(height: 12),
/// Amount Details
Padding(
padding: const EdgeInsets.all(2.0),
child: Row(
children: [
const Text(
"Amount Details",
style: TextStyle(
fontSize: 14,
fontFamily: "JakartaSemiBold",
),
),
const SizedBox(width: 10),
Expanded(
child: DottedLine(
dashGapLength: 4,
dashGapColor: Colors.white,
dashColor: AppColors.grey_semi,
dashLength: 2,
lineThickness: 0.5,
),
),
],
),
),
const SizedBox(height: 6),
_buildKeyValue("Achieved Amount", achieved),
_buildKeyValue("Disbursed Amount", disbursed),
_buildKeyValue("Balance Amount", balance),
const SizedBox(height: 10),
/// Employee Details
Padding(
padding: const EdgeInsets.all(2.0),
child: Row(
children: [
const Text(
"Employee Details",
style: TextStyle(
fontSize: 14,
fontFamily: "JakartaSemiBold",
),
),
const SizedBox(width: 10),
Expanded(
child: DottedLine(
dashGapLength: 4,
dashGapColor: Colors.white,
dashColor: AppColors.grey_semi,
dashLength: 2,
lineThickness: 0.5,
),
),
],
),
),
_buildKeyValue("Entered By", enteredBy),
],
),
);
}
/// Key-Value Row
Widget _buildKeyValue(String key, String value) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 3.5, horizontal: 2),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(key, style: TextStyle(
fontFamily: "JakartaRegular",
fontSize: 14,
color: AppColors.semi_black,
),
),
Text(
value,
style: const TextStyle(
fontSize: 14,
color: Color(0xFF818181),
),
),
],
),
);
}
}
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import '../../Utils/app_colors.dart';
class RewardSearchScreen extends StatefulWidget {
const RewardSearchScreen({super.key});
@override
State<RewardSearchScreen> createState() => _RewardSearchScreenState();
}
class _RewardSearchScreenState extends State<RewardSearchScreen> {
final TextEditingController _searchController = TextEditingController();
// Dummy data
final List<String> accounts = [
"Siddha Bank",
"Siddharth Shivam",
"Sidheshwar Temple",
"Axis Bank",
"SBI",
];
final List<String> inquiries = [
"Inquiry Siddha Bank",
"Inquiry Sidharth",
"Inquiry Sidheshwar",
"Customer SBI",
];
final List<String> leads = [
"Lead Siddha Bank",
"Lead Shivam",
"Lead Sidheshwar",
"Lead HDFC",
];
// Filtered data
String query = "";
@override
Widget build(BuildContext context) {
final filteredAccounts =
accounts.where((e) => e.toLowerCase().contains(query.toLowerCase())).toList();
final filteredInquiries =
inquiries.where((e) => e.toLowerCase().contains(query.toLowerCase())).toList();
final filteredLeads =
leads.where((e) => e.toLowerCase().contains(query.toLowerCase())).toList();
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
backgroundColor: const Color(0xFFFFFFFF),
title: Row(
children: [
InkResponse(
onTap: () => Navigator.pop(context, true),
child: SvgPicture.asset(
"assets/svg/appbar_back_button.svg",
height: 25,
),
),
const SizedBox(width: 10),
const Text(
"Search",
style: TextStyle(
fontSize: 18,
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w600,
color: Colors.black87,
),
),
],
),
),
backgroundColor: Color(0xFFF6F6F8),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
/// Search Field
TextField(
controller: _searchController,
onChanged: (value) {
setState(() {
query = value;
});
},
decoration: InputDecoration(
hintText: "Search",
prefixIcon: const Icon(Icons.search, color: Colors.black54),
filled: true,
fillColor: const Color(0xffFFFFFF),
contentPadding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide.none,
),
),
),
const SizedBox(height: 20),
if (query.isNotEmpty)
Text(
'Result for “$query”',
style: const TextStyle(
fontSize: 18,
color: Color(0xff1487C9),
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 20),
if (query.isNotEmpty && filteredAccounts.isNotEmpty)
_buildSection("Accounts", filteredAccounts),
if (query.isNotEmpty && filteredInquiries.isNotEmpty)
_buildSection("Inquiries", filteredInquiries),
if (query.isNotEmpty && filteredLeads.isNotEmpty)
_buildSection("Leads", filteredLeads),
if (query.isNotEmpty &&
filteredAccounts.isEmpty &&
filteredInquiries.isEmpty &&
filteredLeads.isEmpty)
const Center(
child: Text(
"No results found",
style: TextStyle(fontSize: 14, color: Colors.grey),
),
),
],
),
),
);
}
/// Reusable Section Widget
Widget _buildSection(String title, List<String> items) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
fontFamily: "Plus Jakarta Sans",
color: Color(0xff2D2D2D),
),
),
const SizedBox(height: 10),
Column(
children: items
.map((e) => Container(
margin: const EdgeInsets.only(bottom: 12),
padding: const EdgeInsets.symmetric(
horizontal: 12, vertical: 12),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.1),
blurRadius: 5,
offset: const Offset(0, 3),
)
],
),
child: Row(
children: [
Container(
height: 32,
width: 32,
decoration: BoxDecoration(
color: const Color(0xffE8F3FF),
borderRadius: BorderRadius.circular(8),
),
child: const Icon(Icons.search,
color: Color(0xff0066FF), size: 20),
),
const SizedBox(width: 10),
Expanded(
child: Text(
e,
style: const TextStyle(
fontSize: 14,
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w400,
color: Color(0xff2D2D2D),
),
overflow: TextOverflow.ellipsis,
),
),
],
),
))
.toList(),
)
],
);
}
}
import 'package:dotted_line/dotted_line.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:provider/provider.dart';
import '../../Notifiers/hrmProvider/tourExpensesDetailsProvider.dart';
import '../../Utils/app_colors.dart';
import '../finance/FileViewer.dart';
class TourExpensesDetailsScreen extends StatefulWidget {
final String tourBillId;
const TourExpensesDetailsScreen({Key? key, required this.tourBillId})
: super(key: key);
@override
State<TourExpensesDetailsScreen> createState() => _TourExpensesDetailsScreenState();
}
class _TourExpensesDetailsScreenState extends State<TourExpensesDetailsScreen>{
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (_) => TourExpensesDetailsProvider()
..fetchTourExpensesDetails(context, widget.tourBillId),
child: Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
backgroundColor: Color(0xFFFFFFFF),
title: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
InkResponse(
onTap: () => Navigator.pop(context, true),
child: SvgPicture.asset(
"assets/svg/appbar_back_button.svg",
height: 25,
),
),
SizedBox(width: 10),
InkResponse(
onTap: () => Navigator.pop(context, true),
child: Text(
"Tour Expenses",
style: TextStyle(
fontSize: 18,
height: 1.1,
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w600,
color: AppColors.semi_black,
),
),
),
],
),
),
backgroundColor: AppColors.scaffold_bg_color,
body: Consumer<TourExpensesDetailsProvider>(
builder: (context, provider, child) {
if (provider.isLoading) {
return const Center(child: CircularProgressIndicator());
}
if (provider.errorMessage != null) {
return Center(child: Text(provider.errorMessage!));
}
final response = provider.response;
if (response == null) {
return const Center(child: Text("No data available"));
}
debugPrint("==================requestDetails: ${response.requestDetails?.approvalStatus}");
return SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
/// Header Card at the very top
_expenseHeaderCard(
title: response.requestDetails?.placeOfVisit ?? "Tour",
date: response.tourExpenses?.fromDate ?? "-",
status: (response.requestDetails?.approvalStatus?.isNotEmpty ?? false)
? response.requestDetails!.approvalStatus!
: "No Status",
details: [
{"key": "TL Pending Approval Amount", "value": "-"},
{"key": "Total Approved Amount", "value": response.tourExpenses?.appliedAmount ?? "-"},
{"key": "Total Balance Amount", "value": "-"},
{"key": "HR Expiring Amount (Within 24Hrs)", "value": "-"},
{"key": "HR Pending Approval Amount", "value": "-"},
{"key": "Total Disbursed Amount", "value": "-"},
],
),
const SizedBox(height: 16),
/// Tour Expense Card (Main Summary)
if (response.requestDetails != null && response.tourExpenses != null) ...[
const SizedBox(height: 10),
Padding(
padding: const EdgeInsets.only(left: 30.0),
child: Text("Tour Summary",
style: TextStyle(
fontFamily: "JakartaMedium",
fontSize: 14,
color: AppColors.grey_thick,
),
),
),
const SizedBox(height: 8),
SizedBox(
height: 220, // adjust height to match your card
child: ListView(
scrollDirection: Axis.horizontal,
padding: EdgeInsets.symmetric( // horizontal margin for centering
horizontal: MediaQuery.of(context).size.width * 0.05,
),
children: [
SizedBox(
width: MediaQuery.of(context).size.width * 0.85,
child: _tourExpenseCard(
employeeName: response.requestDetails?.employeeName ?? "-",
placeOfVisit: response.requestDetails?.placeOfVisit ?? "-",
daAmount: response.tourExpenses?.da ?? "0",
totalAmount: response.tourExpenses?.appliedAmount ?? "0",
fromDate: response.tourExpenses?.fromDate ?? "-",
toDate: response.tourExpenses?.toDate ?? "-",
remarks: response.tourExpenses?.extraNote ?? "-",
),
),
],
),
),
],
const SizedBox(height: 10),
/// Travel Expenses Cards
if (response.travelExpenses != null &&
response.travelExpenses!.isNotEmpty) ...[
const SizedBox(height: 10),
Padding(
padding: const EdgeInsets.only(left: 30.0),
child: Text(
"Travel Expenses",
style: TextStyle(
fontFamily: "JakartaMedium",
fontSize: 14,
color: AppColors.grey_thick,
),
),
),
const SizedBox(height: 8),
SizedBox(
height: 216,
child: ListView.separated(
scrollDirection: Axis.horizontal,
padding: EdgeInsets.symmetric(
horizontal: MediaQuery.of(context).size.width * 0.04,
),
itemCount: response.travelExpenses!.length,
separatorBuilder: (_, __) => const SizedBox(width: 12),
itemBuilder: (context, index) {
final t = response.travelExpenses![index];
return SizedBox(
width: MediaQuery.of(context).size.width * 0.90, // card width
child: _travelExpenseCard(
travelType: t.travelType ?? "-",
amount: t.fare ?? "0",
from: t.froma ?? "-",
to: t.toa ?? "-",
onViewTap: () {
debugPrint("Open: ${t.imageDirFilePath}");
//Fileviewer(fileName: "", fileUrl: t.imageDirFilePath.toString())
Navigator.push(
context,
MaterialPageRoute(
builder:
(context) => Image.network(t.imageDirFilePath.toString()),
// Fileviewer(fileName: label, fileUrl: "assets/images/capa.svg"),
),
);
},
),
);
},
),
)
],
const SizedBox(height: 10),
/// Hotel Expenses Cards
if (response.hotelExpenses != null &&
response.hotelExpenses!.isNotEmpty) ...[
const SizedBox(height: 10),
Padding(
padding: const EdgeInsets.only(left: 30.0),
child: Text("Hotel Expenses",
style: TextStyle(
fontFamily: "JakartaMedium",
fontSize: 14,
color: AppColors.grey_thick,
),
),
),
const SizedBox(height: 8),
SizedBox(
height: 216,
child: ListView.separated(
scrollDirection: Axis.horizontal,
padding: EdgeInsets.symmetric(
horizontal: MediaQuery.of(context).size.width * 0.04,
),
itemCount: response.hotelExpenses!.length,
separatorBuilder: (_, __) => const SizedBox(width: 12),
itemBuilder: (context, index) {
final h = response.hotelExpenses![index];
return SizedBox(
width: MediaQuery.of(context).size.width * 0.90,
child: _hotelExpenseCard(
hotelName: h.hotelName ?? "-",
amount: h.amount ?? "0",
fromDate: h.fromDate ?? "-",
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())
),
),
);
},
),
);
},
),
),
],
const SizedBox(height: 10),
/// Other Expenses Cards
if (response.otherExpenses != null &&
response.otherExpenses!.isNotEmpty) ...[
const SizedBox(height: 10),
Padding(
padding: const EdgeInsets.only(left: 30.0),
child: Text("Other Expenses",
style: TextStyle(
fontFamily: "JakartaMedium",
fontSize: 14,
color: AppColors.grey_thick,
),
),
),
const SizedBox(height: 8),
SizedBox(
height: 216,
child: ListView.separated(
scrollDirection: Axis.horizontal,
padding: EdgeInsets.symmetric(
horizontal: MediaQuery.of(context).size.width * 0.04,
),
itemCount: response.otherExpenses!.length,
separatorBuilder: (_, __) => const SizedBox(width: 12),
itemBuilder: (context, index) {
final o = response.otherExpenses![index];
return SizedBox(
width: MediaQuery.of(context).size.width * 0.90,
child: _otherExpenseCard(
description: o.otherDesc ?? "-",
amount: o.otherAmount ?? "0",
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())
),
),
);
},
),
);
},
),
),
],
const SizedBox(height: 25),
],
),
);
},
),
),
);
}
/// Attach your reusable card functions here
Widget _tourExpenseCard({
required String employeeName,
required String placeOfVisit,
required String daAmount,
required String totalAmount,
required String fromDate,
required String toDate,
required String remarks,
}) {
// paste your same implementation here
return Container(
margin: const EdgeInsets.only(bottom: 16),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.1),
blurRadius: 6,
offset: const Offset(0, 3),
)
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
CircleAvatar(
radius: 22.5,
backgroundColor: const Color(0xffFFF3E0),
child: SvgPicture.asset(
"assets/svg/hrm/travel_ic.svg",
),
),
const SizedBox(width: 8),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(employeeName,
style: TextStyle(
fontSize: 14.5,
fontFamily: "JakartaMedium",
fontWeight: FontWeight.w600,
color: AppColors.semi_black,
)
),
Text(placeOfVisit,
style: TextStyle(
fontFamily: "JakartaMedium",
fontSize: 14,
color: AppColors.app_blue,
),
),
],
),
],
),
const SizedBox(height: 12),
_buildSectionHeader("Amount Details"),
_buildKeyValue("DA Amount", daAmount),
_buildKeyValue("Total Amount", totalAmount),
const SizedBox(height: 10),
_buildSectionHeader("Tour Time"),
_buildKeyValue("From Date", fromDate),
_buildKeyValue("To Date", toDate),
const SizedBox(height: 10),
_buildSectionHeader("Remarks"),
_buildKeyValue("Extra Note", remarks),
],
),
);
}
Widget _travelExpenseCard({
required String travelType,
required String amount,
required String from,
required String to,
required VoidCallback onViewTap,
}) {
// paste your travel card code here
return Container(
margin: const EdgeInsets.only(bottom: 16),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.1),
blurRadius: 6,
offset: const Offset(0, 3),
)
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
CircleAvatar(
radius: 20,
backgroundColor: const Color(0xffFFF3E0),
child: SvgPicture.asset(
"assets/svg/hrm/travel_ic.svg",
),
),
const SizedBox(width: 8),
Text(travelType,
style: TextStyle(
fontSize: 14.5,
fontFamily: "JakartaMedium",
fontWeight: FontWeight.w600,
color: AppColors.semi_black,
)
),
],
),
Text("₹$amount",
style: TextStyle(
fontFamily: "JakartaMedium",
fontSize: 14,
color: AppColors.app_blue,
),
),
],
),
const SizedBox(height: 15),
_buildSectionHeader("Travel Details"),
const SizedBox(height: 4),
_buildKeyValue("From", from),
const SizedBox(height: 2),
_buildKeyValue("To", to),
const SizedBox(height: 2),
_buildKeyValue("Image", "View", isLink: true, onTap: onViewTap),
],
),
);
}
Widget _hotelExpenseCard({
required String hotelName,
required String amount,
required String fromDate,
required String toDate,
required VoidCallback onViewTap,
}) {
// paste your hotel card code here
return Container(
margin: const EdgeInsets.only(bottom: 16),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.1),
blurRadius: 6,
offset: const Offset(0, 3),
)
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
CircleAvatar(
radius: 20,
backgroundColor: const Color(0xffFCE4EC),
child:
SvgPicture.asset(
"assets/svg/hrm/hotel_ic.svg",
),
),
const SizedBox(width: 8),
Text(hotelName,
style: TextStyle(
fontSize: 14.5,
fontFamily: "JakartaMedium",
fontWeight: FontWeight.w600,
color: AppColors.semi_black,
)
),
],
),
Text("₹$amount",
style: TextStyle(
fontFamily: "JakartaMedium",
fontSize: 14,
color: AppColors.app_blue,
),
),
],
),
const SizedBox(height: 15),
_buildSectionHeader("Living Details"),
const SizedBox(height: 4),
_buildKeyValue("From", fromDate),
const SizedBox(height: 2),
_buildKeyValue("To", toDate),
const SizedBox(height: 2),
_buildKeyValue("Image", "View", isLink: true, onTap: onViewTap),
],
),
);
}
Widget _otherExpenseCard({
required String description,
required String amount,
required String date,
required VoidCallback onViewTap,
}) {
// paste your other expense card code here
return Container(
margin: const EdgeInsets.only(bottom: 16),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.1),
blurRadius: 6,
offset: const Offset(0, 3),
)
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
CircleAvatar(
radius: 20,
backgroundColor: const Color(0xffEDE7F6),
child: SvgPicture.asset(
"assets/svg/hrm/books_ic.svg",
),
),
const SizedBox(width: 8),
Text(
description,
style: TextStyle(
fontSize: 14.5,
fontFamily: "JakartaMedium",
fontWeight: FontWeight.w600,
color: AppColors.semi_black,
)
),
],
),
Text("₹$amount",
style: TextStyle(
fontFamily: "JakartaMedium",
fontSize: 14,
color: AppColors.app_blue,
),
),
],
),
const SizedBox(height: 15),
_buildSectionHeader("Other Details"),
const SizedBox(height: 4),
_buildKeyValue("Date", date),
const SizedBox(height: 2),
_buildKeyValue("Description", description),
const SizedBox(height: 2),
_buildKeyValue("Image", "View", isLink: true, onTap: onViewTap),
],
),
);
}
Widget _buildSectionHeader(String title) {
return Row(
children: [
Text(title,
style: const TextStyle(
fontSize: 14,
fontFamily: "JakartaSemiBold",
)
),
const SizedBox(width: 16),
Expanded(
child: DottedLine(
dashGapLength: 3,
dashGapColor:
Colors.white,
dashColor: AppColors.grey_semi,
dashLength: 2,
lineThickness: 0.5,
),
)
],
);
}
Widget _buildKeyValue(String key, String value,
{bool isLink = false, VoidCallback? onTap}) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 3.5, horizontal: 2),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(key,
style: TextStyle(
fontFamily: "JakartaRegular",
fontSize: 14,
color: AppColors.semi_black,
),
),
isLink
? GestureDetector(
onTap: onTap,
child: const Text("View",
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Colors.blue)),
)
: Text(value,
style: const TextStyle(
fontSize: 14,
color: Color(0xFF818181),
),
),
],
),
);
}
Widget _expenseHeaderCard({
required String title,
required String date,
required status,
required List<Map<String, String>> details,
}) {
bool showMore = false;
return StatefulBuilder(
builder: (context, setState) {
return Card(
margin: EdgeInsets.symmetric(horizontal: 0, vertical: 1.2),
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(30),
bottomRight: Radius.circular(30),
),
),
elevation: 2,
child: Container(
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(30),
bottomRight: Radius.circular(30),
),
),
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 14),
child: Column(
children: [
/// Header Row
Row(
children: [
Container(
height: 45,
width: 45,
padding: const EdgeInsets.all(7.5),
decoration: const BoxDecoration(
color: Color(0xFFE6F6FF),
shape: BoxShape.circle,
),
child: SvgPicture.asset("assets/svg/hrm/tour_main_ic.svg"),
),
const SizedBox(width: 10),
Expanded(
flex: 5,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title,
style: const TextStyle(
fontSize: 14,
fontFamily: "JakartaRegular",
color: Color(0xff2D2D2D)
)
),
const SizedBox(height: 3),
Text(date,
style: const TextStyle(
fontSize: 12,
color: Color(0xff818181)
)
),
],
),
),
Container(
padding:
const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color: _getStatusBgColor(status),
),
child: Text(status,
style: TextStyle(
fontSize: 14,
fontFamily: "JakartaRegular",
color: _getStatusTxtColor(status))),
)
],
),
const SizedBox(height: 10),
/// Expanded Section
if (showMore) ...[
Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: Row(
children: [
const Text("Amount Details",
style: TextStyle(
fontSize: 14,
fontFamily: "JakartaSemiBold",
)
),
const SizedBox(width: 10),
Expanded(
child: DottedLine(
dashGapLength: 4,
dashGapColor: Colors.white,
dashColor: AppColors.grey_semi,
dashLength: 2,
lineThickness: 0.5,
),
),
],
),
),
const SizedBox(height: 6),
Column(
children: details.map((d) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
flex: 4,
child: Text(d["key"] ?? "-",
style: TextStyle(
fontSize: 14,
color: AppColors.semi_black,
fontFamily: "JakartaRegular",
)
)
),
Expanded(
flex: 3,
child: Text(d["value"] ?? "-",
textAlign: TextAlign.right,
style: const TextStyle(
fontSize: 14,
fontFamily: "JakartaRegular",
color: Color(0xff818181)
)
)
),
],
),
);
}).toList(),
),
],
/// Toggle Button
InkWell(
onTap: () => setState(() => showMore = !showMore),
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(showMore ? "Hide Details" : "View Details",
style: TextStyle(
fontSize: 14,
fontFamily: "JakartaMedium",
fontWeight: FontWeight.w500,
color: AppColors.app_blue,
)
),
const SizedBox(width: 6),
Transform.flip(
flipY: showMore,
child: SvgPicture.asset(
"assets/svg/arrow_dropdown.svg",
height: 25,
width: 20,
color: AppColors.app_blue,
),
)
],
),
),
)
],
),
),
);
},
);
}
/// Avatar color generator
Color _getStatusBgColor(value) {
var color = AppColors.approved_bg_color;
switch (value) {
case 'HR Approved':
return AppColors.approved_bg_color;
case 'Expired at HR':
return AppColors.rejected_bg_color;
case 'Expired at TL':
return AppColors.rejected_bg_color;
}
return color;
}
Color _getStatusTxtColor(value) {
var color = AppColors.approved_text_color;
switch (value) {
case 'HR Approved':
return AppColors.approved_text_color;
case 'Expired at HR':
return AppColors.rejected_text_color;
case 'Expired at TL':
return AppColors.rejected_text_color;
}
return color;
}
getText(value) {
switch (value) {
case 'HR Approved':
return "A";
case 'Expired at HR':
return "E";
case 'Expired at TL':
return "E";
case 'Updated':
return "U";
default:
return "Requested";
}
}
}
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_svg/svg.dart';
import 'package:generp/screens/hrm/TourExpensesDetailsScreen.dart';
import 'package:provider/provider.dart';
import '../../Utils/app_colors.dart';
import '../../Models/hrmModels/tourExpensesListResponse.dart';
import '../../Notifiers/hrmProvider/tourExpensesProvider.dart';
import 'AddTourExpBillScreen.dart';
class TourExpensesListScreen extends StatefulWidget {
const TourExpensesListScreen({super.key});
@override
State<TourExpensesListScreen> createState() => _TourExpensesListScreenState();
}
class _TourExpensesListScreenState extends State<TourExpensesListScreen> {
@override
Widget build(BuildContext context) {
return SafeArea(
top: false,
child: ChangeNotifierProvider(
create: (_) => TourExpensesProvider()..fetchTourExpenses(context, "1"),
child: Consumer<TourExpensesProvider>(
builder: (context, provider, child) {
return Scaffold(
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: 25,
),
),
const SizedBox(width: 10),
const Text(
"Tour Expenses",
style: TextStyle(
fontSize: 18,
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w600,
color: Colors.black87,
),
),
],
),
),
backgroundColor: const Color(0xFFF6F6F8),
body: Column(
children: [
Expanded(
child: Builder(
builder: (context) {
if (provider.isLoading) {
return const Center(
child: CircularProgressIndicator(color: Colors.blue));
}
if (provider.errorMessage != null) {
return Center(child: Text(provider.errorMessage!));
}
if (provider.response?.tourList == null ||
provider.response!.tourList!.isEmpty) {
return const Center(child: Text("No Tour Expenses Found"));
}
final list = provider.response!.tourList!;
return ListView.builder(
padding: const EdgeInsets.all(12),
itemCount: list.length,
itemBuilder: (context, index) {
final TourList item = list[index];
return InkWell(
onTap: () {
/// navigation flow
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => TourExpensesDetailsScreen(
tourBillId: item.id.toString(),
),
),
);
},
child: Container(
margin: const EdgeInsets.symmetric(vertical: 6),
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
),
child: Row(
children: [
/// Left Avatar Circle
Container(
height: 46,
width: 46,
decoration: BoxDecoration(
color: _getAvatarColor(item.approvalStatus),
shape: BoxShape.circle,
),
child: Center(
child: Text(
getText(item.approvalStatus),
style: TextStyle(
fontSize: 15,
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w500,
color: _getAvatarTxtColor(item.approvalStatus),
),
),
),
),
const SizedBox(width: 12),
/// Middle Section
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
item.placeOfVisit ?? "-",
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontFamily: "JakartaRegular",
fontSize: 14,
color: AppColors.semi_black,
),
),
Text(
item.appliedDate ?? "-",
style: TextStyle(
fontFamily: "JakartaRegular",
fontSize: 14,
color: AppColors.grey_semi,
),
),
],
),
),
/// Right Section (Applied Amount)
Text(
"₹${item.appliedAmount ?? '0'}",
style: const TextStyle(
fontFamily: "JakartaMedium",
fontSize: 14,
color: Color(0xff1487c9),
)
),
],
),
),
);
},
);
},
),
),
],
),
floatingActionButtonLocation:
FloatingActionButtonLocation.centerFloat,
floatingActionButton: InkResponse(
onTap: () {
HapticFeedback.selectionClick();
Navigator.push(
context,
MaterialPageRoute(
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(
"Add Bill",
style: TextStyle(
fontSize: 15,
fontFamily: "JakartaMedium",
color: Colors.white,
),
),
),
),
// /// Bottom Add Bill Button
// bottomNavigationBar: Container(
// padding: const EdgeInsets.all(18),
// color: Colors.white,
// child: ElevatedButton(
// style: ElevatedButton.styleFrom(
// backgroundColor: Color(0xff1487c9),
// shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.circular(15),
// ),
// padding: const EdgeInsets.symmetric(vertical: 14),
// ),
// onPressed: () {
// // to work
// },
// child: const Text(
// "Add Bill",
// style: TextStyle(
// fontSize: 16,
// fontFamily: "Plus Jakarta Sans",
// fontWeight: FontWeight.w500,
// color: Colors.white,
// ),
// ),
// ),
// ),
);
},
),
),
);
}
/// Avatar color generator
Color _getAvatarColor(value) {
var color = AppColors.approved_bg_color;
switch (value) {
case 'HR Approved':
return AppColors.approved_bg_color;
case 'Expired at HR':
return AppColors.rejected_bg_color;
case 'Expired at TL':
return AppColors.rejected_bg_color;
}
return color;
}
Color _getAvatarTxtColor(value) {
var color = AppColors.approved_text_color;
switch (value) {
case 'HR Approved':
return AppColors.approved_text_color;
case 'Expired at HR':
return AppColors.rejected_text_color;
case 'Expired at TL':
return AppColors.rejected_text_color;
}
return color;
}
getText(value) {
switch (value) {
case 'HR Approved':
return "A";
case 'Expired at HR':
return "E";
case 'Expired at TL':
return "E";
case 'Updated':
return "U";
default:
return "R";
}
}
}
......@@ -56,3 +56,12 @@ export 'package:generp/Notifiers/crmProvider/addProspectLeadsProvider.dart';
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/attendanceListProvider.dart';
export 'package:generp/Notifiers/hrmProvider/AttendanceDetailsProvider.dart';
export 'package:generp/Notifiers/hrmProvider/tourExpensesProvider.dart';
export 'package:generp/Notifiers/hrmProvider/tourExpensesDetailsProvider.dart';
export 'package:generp/Notifiers/hrmProvider/rewardListProvider.dart';
export 'package:generp/Notifiers/hrmProvider/LeaveApplicationListProvider.dart';
export 'package:generp/Notifiers/hrmProvider/LeaveApplicationDetailsProvider.dart';
......@@ -54,3 +54,5 @@ export 'package:generp/screens/crm/QuotationDetails.dart';
export 'package:generp/screens/crm/contactDetails.dart';
export 'package:generp/screens/crm/editAccountDetails.dart';
export 'package:generp/screens/crm/productDetails.dart';
// hrm screen export
export 'package:generp/screens/hrm/Attendancelist.dart';
......@@ -25,12 +25,20 @@ import 'package:generp/Models/crmModels/crmProspectDetailsAddLeadsResponse.dart'
import 'package:generp/Models/crmModels/crmProspectDetailsResponse.dart';
import 'package:generp/Models/financeModels/addDirectPaymentResponse.dart';
import 'package:generp/Models/financeModels/paymentRequisitionPaymentsListResponse.dart';
import 'package:generp/Models/hrmModels/attendanceRequestListResponse.dart';
import 'package:generp/Models/hrmModels/leaveApplicationDetailsResponse.dart';
import 'package:generp/Models/hrmModels/leaveApplicationLIstResponse.dart';
import 'package:generp/Models/hrmModels/rewardListResponse.dart';
import 'package:generp/Models/hrmModels/tourExpensesAddViewResponse.dart';
import 'package:generp/Models/hrmModels/tourExpensesDetailsResponse.dart';
import 'package:generp/Models/hrmModels/tourExpensesListResponse.dart';
import 'package:generp/Models/ordersModels/PendingTPCAgentListResponse.dart';
import 'package:generp/Models/ordersModels/TPCAgentDetailsResponse.dart';
import 'package:generp/Models/ordersModels/TPCListResponse.dart';
import 'package:generp/Models/ordersModels/orderDashboardResponse.dart';
import 'package:generp/services/api_names.dart';
import 'package:generp/services/api_post_request.dart';
import 'package:http/http.dart' as http;
import 'package:path_provider/path_provider.dart';
import '../Models/AccountSuggestionResponse.dart';
......@@ -92,6 +100,8 @@ import '../Models/financeModels/paymentRequisitionPaymentsDetailsResponse.dart';
import '../Models/financeModels/paymentRequisitionPaymentsReceiptsDetailsResponse.dart';
import '../Models/financeModels/paymentRequisitionPaymentsReceiptsListResponse.dart';
import '../Models/generatorComplaintResponse.dart';
import '../Models/hrmModels/attendanceRequestDetailsResponse.dart';
import '../Models/hrmModels/hrmAccessiblePagesResponse.dart';
import '../Models/loadGeneratorDetailsResponse.dart';
import '../Models/financeModels/financeDashboardPagesResponse.dart';
import '../Models/ordersModels/AddOrderPaymentSelectAccountResponse.dart';
......@@ -116,6 +126,7 @@ import '../Models/ordersModels/paymentListByModeResponse.dart';
import '../Models/ordersModels/technicianAddPaymentResendOTPResponse.dart';
import '../Notifiers/financeProvider/approveRejectPaymentRequestResponse.dart';
import '../Utils/commonServices.dart';
import 'package:http_parser/http_parser.dart';
class ApiCalling {
static Future download_files(empId, session, url, cntxt) async {
......@@ -4898,6 +4909,453 @@ class ApiCalling {
}
}
///hrm modules
///
///
static Future<hrmAccessiblePagesResponse?> hrmAccessiblePagesAPI(
empId,
session,
) async {
try {
Map<String, String> data = {
'emp_id': (empId).toString(),
'session_id': (session).toString(),
};
final res = await post(data, HrmAccessiblePagesUrl, {});
if (res != null) {
print(data);
debugPrint(res.body);
return hrmAccessiblePagesResponse.fromJson(jsonDecode(res.body));
} else {
debugPrint("Null Response");
return null;
}
} catch (e) {
debugPrint('hello bev=bug $e ');
return null;
}
}
static Future<attendanceRequestListResponse?> attendanceRequestListAPI(
empId,
session,
type,
from,
to,
) async {
try {
Map<String, String> data = {
'emp_id': (empId).toString(),
'session_id': (session).toString(),
'type': (type),
'from': (from),
'to': (to),
};
final res = await post(data, AttendanceRequestListUrl, {});
if (res != null) {
print(data);
debugPrint(res.body);
return attendanceRequestListResponse.fromJson(jsonDecode(res.body));
} else {
debugPrint("Null Response");
return null;
}
} catch (e) {
debugPrint('hello bev=bug $e ');
return null;
}
}
static Future<attendanceRequestDetailsResponse?> attendanceRequestDetailAPI(
empId,
session,
attendanceRequestId,
) async {
try {
Map<String, String> data = {
'emp_id': (empId).toString(),
'session_id': (session).toString(),
'attendance_request_id': (attendanceRequestId),
};
final res = await post(data, AttendanceRequestDetailsUrl, {});
if (res != null) {
print(data);
debugPrint(res.body);
return attendanceRequestDetailsResponse.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,
required String process,
required String type,
required String loc,
required String checkDate,
String? checkInTime,
String? checkInLoc,
File? checkInProof,
String? checkOutTime,
String? checkOutLoc,
File? checkOutProof,
String? note,
}) async {
try {
var request = http.MultipartRequest('POST', Uri.parse(AddAttendanceRequestUrl));
// Add basic fields that are always required
Map<String, String> fields = {
"session_id": sessionId,
"emp_id": empId,
"process": process,
"type": type,
"loc": loc,
"check_date": checkDate,
"note": note ?? "",
};
// Conditionally add check-in fields based on type
if (type == "Check In" || type == "Check In/Out") {
fields["check_in_time"] = checkInTime ?? "";
fields["check_in_loc"] = checkInLoc ?? "";
if (checkInProof != null) {
request.files.add(await http.MultipartFile.fromPath("check_in_proof", checkInProof.path));
}
}
// Conditionally add check-out fields based on type
if (type == "Check Out" || type == "Check In/Out") {
fields["check_out_time"] = checkOutTime ?? "";
fields["check_out_loc"] = checkOutLoc ?? "";
if (checkOutProof != null) {
request.files.add(await http.MultipartFile.fromPath("check_out_proof", checkOutProof.path));
}
}
// Add all fields to the request
request.fields.addAll(fields);
// Log the actual fields being sent
debugPrint("addAttendanceRequestAPI - Type: $type");
debugPrint("addAttendanceRequestAPI - Fields: $fields");
debugPrint("addAttendanceRequestAPI - Files: ${request.files.map((f) => f.filename).toList()}");
var response = await request.send();
var resBody = await response.stream.bytesToString();
debugPrint("Server Response: $resBody");
if (response.statusCode == 200) {
return CommonResponse.fromJson(jsonDecode(resBody));
} else {
return null;
}
} catch (e) {
debugPrint("API Error: $e");
return null;
}
}
//reward list
static Future<rewardListResponse?> rewardListAPI(
empId,
session,
) async {
try {
Map<String, String> data = {
'session_id': (session).toString(),
'emp_id': (empId).toString(),
};
final res = await post(data, RewardListUrl, {});
if (res != null) {
print(data);
debugPrint(res.body);
return rewardListResponse.fromJson(jsonDecode(res.body));
} else {
debugPrint("Null Response");
return null;
}
} catch (e) {
debugPrint('hello bev=bug $e ');
return null;
}
}
//tour exp
static Future<tourExpensesListResponse?> tourExpensesListAPI(
empId,
session,
pageNumber,
) async {
try {
Map<String, String> data = {
'session_id': (session).toString(),
'emp_id': (empId).toString(),
'page_number': (pageNumber),
};
final res = await post(data, TourExpensesListUrl, {});
if (res != null) {
print(data);
debugPrint(res.body);
return tourExpensesListResponse.fromJson(jsonDecode(res.body));
} else {
debugPrint("Null Response");
return null;
}
} catch (e) {
debugPrint('hello bev=bug $e ');
return null;
}
}
static Future<tourExpensesDetailsResponse?> tourExpensesDetailAPI(
session,
empId,
tourBillId,
) async {
try {
Map<String, String> data = {
'session_id': (session).toString(),
'emp_id': (empId).toString(),
'tour_bill_id': (tourBillId),
};
final res = await post(data, TourExpensesDetailsUrl, {});
if (res != null) {
print(data);
debugPrint(res.body);
return tourExpensesDetailsResponse.fromJson(jsonDecode(res.body));
} else {
debugPrint("Null Response");
return null;
}
} catch (e) {
debugPrint('hello bev=bug $e ');
return null;
}
}
static Future<tourExpensesAddViewResponse?> tourExpensesAddViewAPI(
empId,
session,
tourBillId,
) async {
try {
Map<String, String> data = {
'session_id': (session).toString(),
'emp_id': (empId).toString(),
'tour_bill_id': (tourBillId),
};
final res = await post(data, TourExpensesAddViewUrl, {});
if (res != null) {
print(data);
debugPrint(res.body);
return tourExpensesAddViewResponse.fromJson(jsonDecode(res.body));
} else {
debugPrint("Null Response");
return null;
}
} catch (e) {
debugPrint('hello bev=bug $e ');
return null;
}
}
static Future<CommonResponse?> addTourBillAPI({
required String sessionId,
required String empId,
required String placeOfVisit,
required String daAmount,
required String tourType,
required String tourDate,
required List<Map<String, dynamic>> travelExpenses,
required List<Map<String, dynamic>> hotelExpenses,
required List<Map<String, dynamic>> otherExpenses,
List<File>? travelImages,
List<File>? hotelImages,
List<File>? otherImages,
}) async {
try {
var request = http.MultipartRequest("POST", Uri.parse(AddTourExpensesUrl));
/// Add text fields
request.fields['session_id'] = sessionId;
request.fields['emp_id'] = empId;
request.fields['place_of_visit'] = placeOfVisit;
request.fields['da_amount'] = daAmount;
request.fields['tour_type'] = tourType;
request.fields['tour_date'] = tourDate;
/// Convert expense lists to JSON string
request.fields['travel_expenses'] = jsonEncode(travelExpenses);
request.fields['hotel_expenses'] = jsonEncode(hotelExpenses);
request.fields['other_expenses'] = jsonEncode(otherExpenses);
/// Add hotel images
if (hotelImages!.isNotEmpty) {
for (var file in hotelImages) {
if (file.path.isNotEmpty) {
request.files.add(
await http.MultipartFile.fromPath(
"hotel_images[]",
file.path,
),
);
}
}
}
/// Add travel images
if (travelImages!.isNotEmpty) {
for (var file in travelImages) {
if (file.path.isNotEmpty) {
request.files.add(
await http.MultipartFile.fromPath(
"travel_images[]",
file.path,
),
);
}
}
}
/// Add other images
if (otherImages!.isNotEmpty) {
for (var file in otherImages) {
if (file.path.isNotEmpty) {
request.files.add(
await http.MultipartFile.fromPath(
"other_images[]",
file.path,
),
);
}
}
}
/// Send request
var response = await request.send();
var resBody = await response.stream.bytesToString();
debugPrint("Request Fields: ${request.fields}");
debugPrint("Response: $resBody");
if (response.statusCode == 200) {
return CommonResponse.fromJson(jsonDecode(resBody));
} else {
debugPrint("Error: ${response.statusCode} - $resBody");
return null;
}
} catch (e) {
debugPrint("Error in addTourBillAPI: $e");
return null;
}
}
// Leave Application api
static Future<leaveApplicationLIstResponse?> leaveApplicationListAPI(
session,
empId,
dateFrom,
dateTo
) async {
try {
Map<String, String> data = {
'session_id': (session).toString(),
'emp_id': (empId).toString(),
'requested_date_from': (dateFrom),
'requested_date_to': (dateTo),
};
final res = await post(data, LeaveApplicationListUrl, {});
if (res != null) {
print(data);
debugPrint(res.body);
return leaveApplicationLIstResponse.fromJson(jsonDecode(res.body));
} else {
debugPrint("Null Response");
return null;
}
} catch (e) {
debugPrint('hello bev=bug $e ');
return null;
}
}
static Future<leaveApplicationDetailsResponse?> leaveApplicationDetailAPI(
session,
empId,
leaveRequestId,
) async {
try {
Map<String, String> data = {
'session_id': (session).toString(),
'emp_id': (empId).toString(),
'leave_request_id': (leaveRequestId),
};
final res = await post(data, LeaveApplicationDetailsUrl, {});
if (res != null) {
print(data);
debugPrint(res.body);
return leaveApplicationDetailsResponse.fromJson(jsonDecode(res.body));
} else {
debugPrint("Null Response");
return null;
}
} catch (e) {
debugPrint('hello bev=bug $e ');
return null;
}
}
//add leave request
static Future<CommonResponse?> leaveRequestAddAPI(
session,
empId,
fromDate,
fromTime,
toDate,
toTime,
leaveType,
reason
) async {
try {
Map<String, String> data = {
'session_id': (session).toString(),
'emp_id': (empId).toString(),
'from_date': (fromDate).toString(),
'from_time': (fromTime).toString(),
'to_date': (toDate).toString(),
'to_time': (toTime).toString(),
'leave_type': (leaveType).toString(),
'reason': (reason).toString(),
};
final res = await post(data, LeaveRequestAdditionUrl, {});
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,
// session,
......
......@@ -178,6 +178,25 @@ const crmDashboardFollowUpUrl = "${baseUrl_test}crm_dashboard_followup_list";
const crmDashboardQuotationsUrl = "${baseUrl_test}crm_dashboard_quotations_list";
///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";
// reward list
const RewardListUrl ="${baseUrl_test}hrm_emp_self_rewards";
// Tour Expenses hrm_emp_self_rewards
const TourExpensesListUrl ="${baseUrl_test}tour_bill_list";
const TourExpensesDetailsUrl ="${baseUrl_test}tour_bill_details";
const TourExpensesAddViewUrl ="${baseUrl_test}add_tour_bill_view";
const AddTourExpensesUrl ="${baseUrl_test}add_tour_bill";
//leave applications
const LeaveApplicationListUrl ="${baseUrl_test}leave_request_list";
const LeaveApplicationDetailsUrl ="${baseUrl_test}leave_request_details";
const LeaveRequestAdditionUrl ="${baseUrl_test}add_leave_request";
......
......@@ -181,6 +181,76 @@ Future<String?> postImageNew(
return null;
}
}
//travel_image
//hotel_image
//other_image
Future<String?> PostMultipleImagesNew(
Map<String, String> body,
String urlLink,
Map<String, String> headers,
List<http.MultipartFile> newList,
List<http.MultipartFile> newList1,
List<http.MultipartFile> newList2,
) async {
try {
var req = http.MultipartRequest('POST', Uri.parse(urlLink));
req.headers.addAll(headers);
req.files.addAll(newList);
req.files.addAll(newList1);
req.files.addAll(newList2);
req.fields.addAll(body);
var res = await req.send();
final resBody = await res.stream.bytesToString();
if (res.statusCode >= 200 && res.statusCode < 300) {
print("**** $resBody .... $res");
return resBody;
} else {
print("error: ${res.reasonPhrase}");
return null;
}
} catch (e) {
debugPrint(e.toString());
return null;
}
}
Future<String?> PostMultipleImagesNew2(
Map<String, String> body,
String urlLink,
Map<String, String> headers,
List<http.MultipartFile> newList,
List<http.MultipartFile> newList1,
) async {
try {
var req = http.MultipartRequest('POST', Uri.parse(urlLink));
req.headers.addAll(headers);
req.files.addAll(newList);
req.files.addAll(newList1);
req.fields.addAll(body);
var res = await req.send();
final resBody = await res.stream.bytesToString();
if (res.statusCode >= 200 && res.statusCode < 300) {
print("**** $resBody .... $res");
return resBody;
} else {
print("error: ${res.reasonPhrase}");
return null;
}
} catch (e) {
debugPrint(e.toString());
return null;
}
}
Future<String?> PostMultipleImages(
Map<String, String> body,
......
......@@ -8,6 +8,7 @@ import Foundation
import app_settings
import connectivity_plus
import device_info_plus
import file_picker
import file_selector_macos
import firebase_core
import firebase_messaging
......@@ -30,6 +31,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
AppSettingsPlugin.register(with: registry.registrar(forPlugin: "AppSettingsPlugin"))
ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin"))
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin"))
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin"))
FLTFirebaseMessagingPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseMessagingPlugin"))
......
......@@ -465,6 +465,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "7.0.1"
file_picker:
dependency: "direct main"
description:
name: file_picker
sha256: ab13ae8ef5580a411c458d6207b6774a6c237d77ac37011b13994879f68a8810
url: "https://pub.dev"
source: hosted
version: "8.3.7"
file_selector_linux:
dependency: transitive
description:
......
......@@ -89,6 +89,7 @@ dependencies:
pinput: ^5.0.1
build_runner: ^2.4.0
build_web_compilers: ^4.0.4
file_picker: ^8.0.0
dev_dependencies:
flutter_test:
......@@ -126,6 +127,7 @@ flutter:
- assets/svg/service/
- assets/svg/crm/
- assets/svg/order/
- assets/svg/hrm/
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/to/resolution-aware-images
......