forex calculations

This commit is contained in:
venbaittech 2025-05-26 10:59:01 +05:30
parent c99ac679a6
commit 42f3f465d5
10 changed files with 583 additions and 281 deletions

View File

@ -369,7 +369,7 @@ class _LoginWidgetState extends State<LoginWidget> {
),
const SizedBox(height: 2),
Text(
"Welcome To Trip Approval Tools",
"Welcome To TripApprovalTool",
style: GoogleFonts.poppins(
fontSize: 11,
fontWeight: FontWeight.w400,
@ -822,55 +822,48 @@ class _LoginWidgetState extends State<LoginWidget> {
);
}
Future<void> handleMS() async {
final url = '$apiUrl/auth/mslogin';
print(url);
try {
final response = await http
.get(Uri.parse(url), headers: {'Content-Type': 'application/json'});
print("inside try method");
if (response.statusCode == 200) {
final authUrl = json.decode(response.body)['auth_url'];
print("authurl - $authUrl");
if (authUrl != '') {
// final prefs = await SharedPreferences.getInstance();
// await prefs.setString('auth_token', authUrl);
print('i have auth URL');
// canLaunchUrl(authUrl);
final url = '$apiUrl/auth/mslogin';
print(url);
try {
final response = await http.get(
Uri.parse(url),
headers: { 'Content-Type': 'application/json' }
);
print("inside try method");
if (response.statusCode == 200) {
final authUrl = json.decode(response.body)['auth_url'];
print("authurl - $authUrl");
if(authUrl != ''){
// final prefs = await SharedPreferences.getInstance();
// await prefs.setString('auth_token', authUrl);
print('i have auth URL');
// canLaunchUrl(authUrl);
if (kIsWeb) {
print("kIsWeb");
// Use web redirect (e.g., via JS interop or window.location.href)
// redirectTo(url);
if (kIsWeb) {
print("kIsWeb");
// Use web redirect (e.g., via JS interop or window.location.href)
// redirectTo(url);
html.window.location.href = authUrl;
} else {
// For mobile/desktop, open in external browser
// launchUrl(Uri.parse(url),
// mode: LaunchMode.externalApplication);
}
// final result = await FlutterWebAuth.authenticate(
// url: authUrl,
// callbackUrlScheme: "myapp", // Use a custom scheme you registered
// );
}else{
print('auth URL not Founded');
throw Exception('auth URL not Founded');
html.window.location.href = authUrl;
} else {
// For mobile/desktop, open in external browser
// launchUrl(Uri.parse(url),
// mode: LaunchMode.externalApplication);
}
}else{
final errorMessage = json.decode(response.body)['message'];
print(errorMessage);
throw Exception(errorMessage);
// final result = await FlutterWebAuth.authenticate(
// url: authUrl,
// callbackUrlScheme: "myapp", // Use a custom scheme you registered
// );
} else {
print('auth URL not Founded');
throw Exception('auth URL not Founded');
}
} else {
final errorMessage = json.decode(response.body)['message'];
print(errorMessage);
throw Exception(errorMessage);
}
catch (e) {
print("Error: $e");
}
} catch (e) {
print("Error: $e");
}
}
}

View File

@ -25,7 +25,7 @@ class DepartmentList extends StatefulWidget {
class DepartmentListState extends State<DepartmentList> {
final GlobalKey<DepartmentListState> departmentListKey =
GlobalKey<DepartmentListState>();
GlobalKey<DepartmentListState>();
final ApiService apiService = ApiService();
late Future<List<dynamic>> futureDepartment;
@ -97,7 +97,6 @@ class DepartmentListState extends State<DepartmentList> {
}
Future<List<dynamic>> fetchGetDepartment() async {
final String apiUrlData = '$apiUrl/api/getDepartmentList';
final String? token = await getToken();
@ -142,19 +141,18 @@ class DepartmentListState extends State<DepartmentList> {
print("all before filtering: $query");
final lowerQuery = query.toLowerCase();
setState(() {
filteredDepartment = allDepartment.where((object) {
final isActiveStatus =
object['is_active'] == "1" ? "active" : "inactive";
return (object['department_id']?.toLowerCase().contains(lowerQuery) ??
false) ||
(object['name']?.toLowerCase().contains(lowerQuery) ??
false) ||
(object['description']?.toLowerCase().contains(lowerQuery) ?? false) ||
(isActiveStatus.contains(lowerQuery));
}).toList();
filteredDepartment = allDepartment.where((object) {
final isActiveStatus =
object['is_active'] == "1" ? "active" : "inactive";
return (object['department_id']?.toLowerCase().contains(lowerQuery) ??
false) ||
(object['name']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['description']?.toLowerCase().contains(lowerQuery) ??
false) ||
(isActiveStatus.contains(lowerQuery));
}).toList();
});
print("filteredDepartment: $filteredDepartment");
}
@override
@ -171,11 +169,11 @@ class DepartmentListState extends State<DepartmentList> {
body: Padding(
padding: isDesktop
? EdgeInsets.symmetric(
horizontal: MediaQuery.of(context).size.width *
0.1, // 30% of screen width as horizontal padding
vertical: MediaQuery.of(context).size.height *
0, // 5% of screen height as vertical padding
)
horizontal: MediaQuery.of(context).size.width *
0.1, // 30% of screen width as horizontal padding
vertical: MediaQuery.of(context).size.height *
0, // 5% of screen height as vertical padding
)
: EdgeInsets.all(0),
child: Row(
children: [
@ -297,7 +295,7 @@ class DepartmentListState extends State<DepartmentList> {
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side:
BorderSide(color: Color(0xFF114D8B), width: 2),
BorderSide(color: Color(0xFF114D8B), width: 2),
),
padding: EdgeInsets.symmetric(
horizontal: 20, vertical: 12),
@ -317,7 +315,7 @@ class DepartmentListState extends State<DepartmentList> {
},
child: Row(
mainAxisSize:
MainAxisSize.min, // Ensures content fits nicely
MainAxisSize.min, // Ensures content fits nicely
children: [
Text(
"Add Department",
@ -344,46 +342,46 @@ class DepartmentListState extends State<DepartmentList> {
isDesktop
? SizedBox.shrink()
: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
width: MediaQuery.of(context).size.width * 0.8,
height: 35,
child: TextField(
controller: searchController,
onChanged: filterDepartment,
decoration: InputDecoration(
hintText: "Search ...",
hintStyle: TextStyle(
fontSize: 12, color: Color(0xFF9E9DBD)),
prefixIcon: Icon(
Icons.search,
color: Color(0xFF9E9DBD),
size: 18,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
width: MediaQuery.of(context).size.width * 0.8,
height: 35,
child: TextField(
controller: searchController,
onChanged: filterDepartment,
decoration: InputDecoration(
hintText: "Search ...",
hintStyle: TextStyle(
fontSize: 12, color: Color(0xFF9E9DBD)),
prefixIcon: Icon(
Icons.search,
color: Color(0xFF9E9DBD),
size: 18,
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(
color: Colors.grey.shade200,
width: 0.5),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(
color: Colors.grey.shade300, width: 1),
),
),
style: GoogleFonts.poppins(
fontSize: 12,
),
),
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(
color: Colors.grey.shade200,
width: 0.5),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(
color: Colors.grey.shade300, width: 1),
),
),
style: GoogleFonts.poppins(
fontSize: 12,
),
// SizedBox(width: 16),
],
),
),
// SizedBox(width: 16),
],
),
const SizedBox(height: 10),
FutureBuilder<List<dynamic>>(
future: futureDepartment,
@ -432,8 +430,9 @@ class DepartmentListState extends State<DepartmentList> {
);
}
/* Here collect the list to displayed the data in table or card Used */
List<dynamic> object =
filteredDepartment.isNotEmpty ? filteredDepartment : allDepartment;
List<dynamic> object = filteredDepartment.isNotEmpty
? filteredDepartment
: allDepartment;
/* List is Sorting here */
object.sort((a, b) {
@ -454,7 +453,7 @@ class DepartmentListState extends State<DepartmentList> {
Widget table = LayoutBuilder(
builder: (context, constraints) {
double minWidth =
isDesktop ? constraints.maxWidth : 1300;
isDesktop ? constraints.maxWidth : 1300;
return ConstrainedBox(
constraints: BoxConstraints(minWidth: minWidth),
@ -468,64 +467,67 @@ class DepartmentListState extends State<DepartmentList> {
columns: [
DataColumn(
label: Text(
'Department ID',
style: GoogleFonts.poppins(
fontSize: 13,
fontWeight: FontWeight.w600),
)),
'Department ID',
style: GoogleFonts.poppins(
fontSize: 13,
fontWeight: FontWeight.w600),
)),
DataColumn(
label: Text(
'Name',
style: GoogleFonts.poppins(
fontSize: 13,
fontWeight: FontWeight.w600),
)),
'Name',
style: GoogleFonts.poppins(
fontSize: 13,
fontWeight: FontWeight.w600),
)),
DataColumn(
label: Text(
'Description',
style: GoogleFonts.poppins(
fontSize: 13,
fontWeight: FontWeight.w600),
)),
'Description',
style: GoogleFonts.poppins(
fontSize: 13,
fontWeight: FontWeight.w600),
)),
DataColumn(
label: Text(
'Status',
style: GoogleFonts.poppins(
fontSize: 13,
fontWeight: FontWeight.w600),
)),
'Status',
style: GoogleFonts.poppins(
fontSize: 13,
fontWeight: FontWeight.w600),
)),
DataColumn(
label: Text(
'Actions',
style: GoogleFonts.poppins(
fontSize: 13,
fontWeight: FontWeight.w600),
)),
'Actions',
style: GoogleFonts.poppins(
fontSize: 13,
fontWeight: FontWeight.w600),
)),
],
rows: paginatedDepartment.map((tableObject) {
String departmentId = tableObject['department_id']
.toString(); // Get user ID
bool isSelected = selectedDepartmentId == departmentId;
String departmentId =
tableObject['department_id']
.toString(); // Get user ID
bool isSelected =
selectedDepartmentId == departmentId;
return DataRow(cells: [
DataCell(
Text("${tableObject['department_id'] ?? ''}",
style: TextStyle(
fontSize: 13,
fontFamily: "Inter",
))),
DataCell(Text(
"${tableObject['department_id'] ?? ''}",
style: TextStyle(
fontSize: 13,
fontFamily: "Inter",
))),
DataCell(Text(tableObject['name'] ?? '',
style: TextStyle(
fontSize: 13,
fontFamily: "Inter",
))),
DataCell(Text(tableObject['description'] ?? 'N/A',
style: TextStyle(
fontSize: 13,
fontFamily: "Inter",
),
softWrap: true,
overflow: TextOverflow.ellipsis)),
DataCell(
Text(tableObject['description'] ?? 'N/A',
style: TextStyle(
fontSize: 13,
fontFamily: "Inter",
),
softWrap: true,
overflow: TextOverflow.ellipsis)),
DataCell(
Text(
tableObject['is_active'] == "1"
@ -534,7 +536,9 @@ class DepartmentListState extends State<DepartmentList> {
style: TextStyle(
fontSize: 13,
fontFamily: "Inter",
color: tableObject['is_active'] == "1" ? Colors.green : Colors.red,
color: tableObject['is_active'] == "1"
? Colors.green
: Colors.red,
),
softWrap: true,
overflow: TextOverflow.ellipsis,
@ -558,17 +562,21 @@ class DepartmentListState extends State<DepartmentList> {
final departmentId = int.tryParse(
tableObject['department_id']
.toString());
if (departmentId != null) {
print("Table cell - department Id -- $departmentId");
final data = await apiService.getDepartmentDetailsFind(departmentId);
print(
"Table cell - department Id -- $departmentId");
final data = await apiService
.getDepartmentDetailsFind(
departmentId);
print("DepartmentId -- $data");
showDialog(
context: context,
builder: (context) => DepartmentData(
builder: (context) =>
DepartmentData(
isDesktop: isDesktop,
departmentId: departmentId, // Pass the ID
departmentId:
departmentId, // Pass the ID
departmentData: data,
layoutColor: layoutColor!,
// fetchGetForex: fetchGetForex,
@ -612,7 +620,7 @@ class DepartmentListState extends State<DepartmentList> {
// Status and Employee Code
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
MainAxisAlignment.spaceBetween,
children: [
Text(
cardObject['department_id'] ?? 'N/A',
@ -636,20 +644,25 @@ class DepartmentListState extends State<DepartmentList> {
.toString());
if (departmentId != null) {
print("departmentId -- $departmentId");
print(
"departmentId -- $departmentId");
final data = await apiService
.getDepartmentDetailsFind(departmentId);
.getDepartmentDetailsFind(
departmentId);
print("DepartmentId -- $data");
showDialog(
context: context,
builder: (context) => DepartmentData(
builder: (context) =>
DepartmentData(
isDesktop: isDesktop,
departmentId:departmentId, // Pass the ID
departmentData:data,
layoutColor:layoutColor!,
departmentId:
departmentId, // Pass the ID
departmentData: data,
layoutColor: layoutColor!,
// fetchGetDepartment: fetchGetDepartment,
fetchGetDepartment: refreshData,
fetchGetDepartment:
refreshData,
// role:
// "Travel Agent"
),
@ -742,7 +755,7 @@ class DepartmentListState extends State<DepartmentList> {
children: [
Column(
crossAxisAlignment:
CrossAxisAlignment.start,
CrossAxisAlignment.start,
children: [
Text(
cardObject['name'] ?? '',
@ -757,7 +770,7 @@ class DepartmentListState extends State<DepartmentList> {
),
Column(
crossAxisAlignment:
CrossAxisAlignment.start,
CrossAxisAlignment.start,
children: [
Text(
cardObject['description'] ?? '',
@ -779,39 +792,39 @@ class DepartmentListState extends State<DepartmentList> {
);
}
return Expanded(
child: Column(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: isDesktop
? (searchController.text.isNotEmpty &&
filteredDepartment.isEmpty
? Center(
child: Text(
"No matches found",
style: GoogleFonts.poppins(
fontSize: 14,
color: Colors.grey),
),
)
: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: table,
))
: (searchController.text.isNotEmpty &&
filteredDepartment.isEmpty
? Center(
child: Text(
"No matches found",
style: GoogleFonts.poppins(
fontSize: 14,
color: Colors.grey),
),
)
: buildMobileCardView(paginatedDepartment)),
),
Expanded(
child: isDesktop
? (searchController.text.isNotEmpty &&
filteredDepartment.isEmpty
? Center(
child: Text(
"No matches found",
style: GoogleFonts.poppins(
fontSize: 14,
color: Colors.grey),
),
)
: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: table,
))
: (searchController.text.isNotEmpty &&
filteredDepartment.isEmpty
? Center(
child: Text(
"No matches found",
style: GoogleFonts.poppins(
fontSize: 14,
color: Colors.grey),
),
)
: buildMobileCardView(
paginatedDepartment)),
),
// Expanded(
// child: isDesktop
// ? SingleChildScrollView(
@ -846,4 +859,4 @@ class DepartmentListState extends State<DepartmentList> {
)),
);
}
}
}

View File

@ -3,6 +3,7 @@ import 'dart:convert';
import 'package:dropdown_search/dropdown_search.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:http/http.dart' as http;
@ -51,11 +52,16 @@ class ForexDataState extends State<ForexData> {
int? forexDataId;
late String isActive = "1";
bool isCashEditing = false;
bool isCardEditing = false;
List<String> dataHeader = [
"country_code",
"country",
"currency",
"perdiemAmount"
"perdiemAmount",
"cash",
"card"
];
Map<String, dynamic> forex_Detials() {
@ -64,6 +70,8 @@ class ForexDataState extends State<ForexData> {
"country_code": selectedCountry,
"country_name": selectedCountryName,
"currency": controllers["currency"]?.text,
"cash_percentage": controllers["cash"]?.text,
"card_percentage": controllers["card"]?.text,
"perdiem_amount": controllers["perdiemAmount"]?.text,
"is_active": 1,
"created_by": userId,
@ -82,6 +90,10 @@ class ForexDataState extends State<ForexData> {
controllers[field] = TextEditingController();
}
fetchCountries();
controllers["cash"]?.addListener(_handleCashChange);
controllers["card"]?.addListener(_handleCardChange);
if (widget.forexId != null) {
print('Editing Forex ID: ${widget.forexId}');
updateForexDetails();
@ -116,6 +128,8 @@ class ForexDataState extends State<ForexData> {
selectedCurrency = data['currency']; // Optional if used elsewhere
controllers['currency']?.text = data['currency'] ?? '';
controllers['cash']?.text = data['cash_percentage'] ?? '';
controllers['card']?.text = data['card_percentage'] ?? '';
controllers['perdiemAmount']?.text = data['perdiem_amount'].toString();
isActive = data["is_active"];
final forexId = int.tryParse(data['forex_perdiem_id'].toString());
@ -140,6 +154,31 @@ class ForexDataState extends State<ForexData> {
});
}
void _handleCashChange() {
if (isCardEditing) return; // Prevent circular update
isCashEditing = true;
final cashText = controllers["cash"]?.text ?? '';
final cash = int.tryParse(cashText) ?? 0;
final card = 100 - cash;
controllers["card"]?.text = card.toString();
isCashEditing = false;
}
void _handleCardChange() {
if (isCashEditing) return; // Prevent circular update
final cash = int.tryParse(controllers["cash"]?.text ?? '') ?? 0;
final card = int.tryParse(controllers["card"]?.text ?? '') ?? 0;
if (cash + card != 100) {
errorMessages["cash_percentage"] = "Cash and card must total 100%";
} else {
errorMessages.remove("cash_percentage");
}
}
bool validateData() {
errorMessages.clear();
@ -147,6 +186,8 @@ class ForexDataState extends State<ForexData> {
"country_code": selectedCountry,
"country": selectedCountryName,
"currency": controllers["currency"]?.text,
"cash_percentage": controllers["cash"]?.text,
"card_percentage": controllers["card"]?.text,
"perdiemAmount": controllers["perdiemAmount"]?.text,
};
@ -154,7 +195,9 @@ class ForexDataState extends State<ForexData> {
"country_code",
"country",
"currency",
"perdiemAmount"
"perdiemAmount",
"cash_percentage",
"card_percentage"
];
// Check validation for each field
@ -164,6 +207,13 @@ class ForexDataState extends State<ForexData> {
}
}
final cash = int.tryParse(data["cash_percentage"] ?? '') ?? 0;
final card = int.tryParse(data["card_percentage"] ?? '') ?? 0;
if (cash + card != 100) {
errorMessages["card_percentage"] = "Total must be 100%";
}
return errorMessages.isEmpty;
}
@ -431,6 +481,107 @@ class ForexDataState extends State<ForexData> {
),
// if (isDesktop)SizedBox(width: 8,) else SizedBox(height: 8,),
SizedBox(
height: 10,
),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Cash",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldForexWrapper(
isFocused: false,
isDesktop: widget.isDesktop,
color: Colors.transparent,
width: widget.isDesktop
? MediaQuery.of(context).size.width * 0.09
: MediaQuery.of(context).size.width * 0.66,
child: SizedBox(
height: 40,
child: TextField(
controller: controllers["cash"],
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
],
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "Cash",
labelStyle:
TextStyle(fontSize: 11, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
),
)),
),
if (errorMessages["cash_percentage"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
errorMessages["cash_percentage"]!,
style: TextStyle(color: Colors.red, fontSize: 12),
),
],
],
),
Spacer(),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Card",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldForexWrapper(
isFocused: false,
isDesktop: widget.isDesktop,
color: Colors.transparent,
width: widget.isDesktop
? MediaQuery.of(context).size.width * 0.09
: MediaQuery.of(context).size.width * 0.66,
child: SizedBox(
height: 40,
child: TextField(
controller: controllers["card"],
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
],
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "Card",
labelStyle:
TextStyle(fontSize: 11, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
),
)),
),
if (errorMessages["card_percentage"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
errorMessages["card_percentage"]!,
style: TextStyle(color: Colors.red, fontSize: 12),
),
],
],
)
],
),
SizedBox(
height: 10,
),

View File

@ -3,6 +3,7 @@ import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:frontend/Screens/group/group.dart';
import 'package:go_router/go_router.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:http/http.dart' as http;
import 'package:responsive_builder/responsive_builder.dart';
@ -209,9 +210,14 @@ class _GroupListState extends State<GroupList> {
children: [
Row(
children: [
const Text('Group List',
style:
TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
Text(
'Group List',
style: GoogleFonts.poppins(
fontSize: isDesktop ? 16 : 14,
fontWeight: FontWeight.w600,
color: Colors.black,
),
),
IconButton(
icon: const Icon(Icons.keyboard_arrow_down),
onPressed: () {},
@ -234,7 +240,7 @@ class _GroupListState extends State<GroupList> {
},
child: Row(
children: [
Text('New Group'),
Text('New Group', style: GoogleFonts.poppins(fontSize: 12)),
SizedBox(
width: 5,
),
@ -300,9 +306,24 @@ class _GroupListState extends State<GroupList> {
Row(
children: [
Expanded(
child: Text("Group Name: ${group['name']}",
style: TextStyle(
fontSize: 13, fontWeight: FontWeight.bold)),
child: Text("Group Name",
style: GoogleFonts.poppins(
fontSize: 11.5, fontWeight: FontWeight.w400)),
),
Expanded(
child: Text("Domestic Policy",
style: GoogleFonts.poppins(
fontSize: 11.5, fontWeight: FontWeight.w400)),
),
Expanded(
child: Text("International Policy",
style: GoogleFonts.poppins(
fontSize: 11.5, fontWeight: FontWeight.w400)),
),
Expanded(
child: Text("Description",
style: GoogleFonts.poppins(
fontSize: 11.5, fontWeight: FontWeight.w400)),
),
],
),
@ -311,9 +332,23 @@ class _GroupListState extends State<GroupList> {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(
"Policy: ${group['domestic_policy_name'] ?? group['international_policy_name']}")),
Expanded(child: Text("${group['description'] ?? 'N/A'}")),
child: Text("${group['name']}",
style: GoogleFonts.poppins(
fontSize: 13, fontWeight: FontWeight.w600)),
),
Expanded(
child: Text("${group['domestic_policy_name'] ?? 'N/A'}",
style: GoogleFonts.poppins(
fontSize: 13, fontWeight: FontWeight.w600)),
),
Expanded(
child: Text("${group['international_policy_name']}",
style: GoogleFonts.poppins(
fontSize: 13, fontWeight: FontWeight.w600))),
Expanded(
child: Text("${group['description'] ?? 'N/A'}",
style: GoogleFonts.poppins(
fontSize: 13, fontWeight: FontWeight.w600))),
],
),
Row(

View File

@ -42,8 +42,10 @@ class _ForexScreenState extends State<ForexScreen> {
late ValueNotifier<String?> flightFirstTripDateNotifier;
late ValueNotifier<String?> flightLastTripDateNotifier;
// late final tripuserId;
String? tripuserId;
late String? userCardNumber;
// late String? userCardNumber;
Map<String, String?> selectedValues = {};
bool isChecked = false; // State variable for checkbox
@ -97,6 +99,10 @@ class _ForexScreenState extends State<ForexScreen> {
String? selectedPerdiemAmount;
String? CalculatedOtherExpenses;
String? selectedQuotedAmount;
int? selectedCashPercent;
int? selectedCardPercent;
bool userEdited = false;
Map<String, dynamic> get forexData {
Map<String, dynamic> data = {
@ -116,6 +122,8 @@ class _ForexScreenState extends State<ForexScreen> {
"delivery_location": textControllers["_deliveryLocation"]?.text,
"comments": textControllers["_comments"]?.text,
"total": selectedQuotedAmount,
"card_percentage": selectedCardPercent,
"cash_percentage": selectedCashPercent,
"created_by": widget.loginUser,
"updated_by": widget.loginUser,
};
@ -138,6 +146,7 @@ class _ForexScreenState extends State<ForexScreen> {
"country_code": selectedCountry,
"start_date": _formatDate(textControllers["_forexStartDate"]?.text),
"end_date": _formatDate(textControllers["_forexEndDate"]?.text),
"user_id": tripuserId
// "currency": selectedCurrency ?? "",
};
}
@ -186,6 +195,13 @@ class _ForexScreenState extends State<ForexScreen> {
selectedDuration = responseData["duration"]?.toString() ?? "";
selectedQuotedAmount =
responseData["perdiem_amount"]?.toString() ?? "";
selectedCardPercent =
int.tryParse(responseData["card_percentage"]?.toString() ?? "");
selectedCashPercent =
int.tryParse(responseData["cash_percentage"]?.toString() ?? "");
textControllers["_cardNumber"]?.text =
responseData["forex_card_no"]?.toString() ?? "";
});
_onFieldChangedForOthers();
_divideQuotedAmount();
@ -325,7 +341,10 @@ class _ForexScreenState extends State<ForexScreen> {
flightFirstTripDateNotifier = ValueNotifier<String?>(null);
flightLastTripDateNotifier = ValueNotifier<String?>(null);
WidgetsBinding.instance.addPostFrameCallback((_) {
WidgetsBinding.instance.addPostFrameCallback((_) async {
tripuserId = await getTripUserId();
print("tripuserId - $tripuserId");
final result = getFlightTripDateRange(widget.flightData);
flightFirstTripDateNotifier.value = result['firstTripDate'];
flightLastTripDateNotifier.value = result['lastTripDate'];
@ -351,15 +370,15 @@ class _ForexScreenState extends State<ForexScreen> {
void handleUpdatedField() async {
// Set the selected value if available
userCardNumber = await getForexCardNumber();
// userCardNumber = "CD7909043";
print("userCardNumber - $userCardNumber");
//
// userCardNumber = await getForexCardNumber();
// // userCardNumber = "CD7909043";
// print("userCardNumber - $userCardNumber");
if (widget.selectedItem == null &&
textControllers["_cardNumber"]?.text == "") {
print("userCardNumber11 - $userCardNumber");
textControllers["_cardNumber"]?.text = userCardNumber ?? "";
// print("userCardNumber11 - $userCardNumber");
// textControllers["_cardNumber"]?.text = userCardNumber ?? "";
}
if (widget.selectedItem != null) {
@ -381,13 +400,20 @@ class _ForexScreenState extends State<ForexScreen> {
selectedCountry = widget.selectedItem!["country_code"] as String?;
selectedCurrency = widget.selectedItem!["currency"] as String?;
selectedDuration = widget.selectedItem!["duration"] as String?;
selectedCardPercent = int.tryParse(
widget.selectedItem!["card_percentage"]?.toString() ?? "");
selectedCashPercent = int.tryParse(
widget.selectedItem!["cash_percentage"]?.toString() ?? "");
selectedPerdiemAmount = widget.selectedItem!["perdiem_amount"] as String?;
isChecked =
widget.selectedItem!["have_card"] == "1"; // Convert string to bool
textControllers["_cardNumber"]?.text =
widget.selectedItem!["card_number"]?.toString() ?? "";
// if (textControllers["_cardNumber"] != null) {
// print("userCardNumber11 - $userCardNumber");
// textControllers["_cardNumber"]!.text = userCardNumber ?? '';
//
// }
_onFieldChangedForOthers();
@ -431,7 +457,11 @@ class _ForexScreenState extends State<ForexScreen> {
}
if (_isForexDataComplete()) {
postgetForexData(getForexData);
if (tripuserId != null) {
postgetForexData(getForexData);
} else {
print("tripuserId is null");
}
}
}
@ -503,35 +533,42 @@ class _ForexScreenState extends State<ForexScreen> {
selectedQuotedAmount =
((perdiemAmount + calclateVal).toString() ?? 0) as String?;
});
_divideQuotedAmount();
if (userEdited) {
_divideQuotedAmount();
}
errorMessages.clear();
}
void _divideQuotedAmount() {
int? quotedAmount = int.tryParse(selectedQuotedAmount!);
print("quotedAmount - $selectedQuotedAmount");
print("DIVIDREFD - $selectedQuotedAmount -$quotedAmount");
if (quotedAmount != null) {
fifteenPercent =
(quotedAmount * 15) ~/ 100; // Calculate 15% (integer division)
// fifteenPercent = (quotedAmount * 15) ~/ 100;
print("selectedCardPercent - $selectedCashPercent");
fifteenPercent = (quotedAmount * selectedCashPercent!) ~/
100; // Calculate 15% (integer division)
remainingAmount = quotedAmount - fifteenPercent; // Subtract from total
// Only set text if the field is empty (user hasn't typed)
if (textControllers["_cash"] != null &&
textControllers["_cash"]!.text.trim().isEmpty) {
textControllers["_cash"]!.text = fifteenPercent.toString();
} else {
print("_cash already has user input, not overwriting");
}
// // Only set text if the field is empty (user hasn't typed)
// if (textControllers["_cash"] != null &&
// textControllers["_cash"]!.text.trim().isEmpty) {
// textControllers["_cash"]!.text = fifteenPercent.toString();
// } else {
// print("_cash already has user input, not overwriting");
// }
//
// if (textControllers["_card"] != null &&
// textControllers["_card"]!.text.trim().isEmpty) {
// textControllers["_card"]!.text = remainingAmount.toString();
// } else {
// print("_card already has user input, not overwriting");
// }
if (textControllers["_card"] != null &&
textControllers["_card"]!.text.trim().isEmpty) {
textControllers["_card"]!.text = remainingAmount.toString();
} else {
print("_card already has user input, not overwriting");
}
// textControllers["_cash"]?.text = fifteenPercent.toString();
// textControllers["_card"]?.text = remainingAmount.toString();
textControllers["_cash"]?.text = fifteenPercent.toString();
textControllers["_card"]?.text = remainingAmount.toString();
print("15% Amount: $fifteenPercent");
print("Remaining Amount: $remainingAmount");
} else {
@ -588,13 +625,13 @@ class _ForexScreenState extends State<ForexScreen> {
print('CardAmount - $cardAmount');
textControllers["_card"]?.text = difference.toString();
if (enteredAmount == null || enteredAmount > fifteenPercent) {
errorMessages["deposit_on_cash"] = "Amount cannot exceed $fifteenPercent";
} else if (checkValidAmount == quotedAmount) {
errorMessages["deposit_on_card"] = " "; // Clear error if valid
} else {
errorMessages["deposit_on_cash"] = ""; // Clear error if valid
}
// if (enteredAmount > fifteenPercent) {
// errorMessages["deposit_on_cash"] = "Amount cannot exceed $fifteenPercent";
// } else if (checkValidAmount == quotedAmount) {
// errorMessages["deposit_on_card"] = " "; // Clear error if valid
// } else {
// errorMessages["deposit_on_cash"] = ""; // Clear error if valid
// }
// Refresh UI if using StatefulWidget
setState(() {});
@ -1253,7 +1290,12 @@ class _ForexScreenState extends State<ForexScreen> {
child: TextField(
focusNode: focusNodes["_transport"],
controller: textControllers["_transport"],
onChanged: (value) => _onFieldChangedForOthers(),
onChanged: (value) {
setState(() {
userEdited = true;
});
_onFieldChangedForOthers();
},
keyboardType: TextInputType.numberWithOptions(decimal: true),
inputFormatters: [
FilteringTextInputFormatter.allow(RegExp(
@ -1324,7 +1366,12 @@ class _ForexScreenState extends State<ForexScreen> {
child: TextField(
focusNode: focusNodes["_accomodation"],
controller: textControllers["_accomodation"],
onChanged: (value) => _onFieldChangedForOthers(),
onChanged: (value) {
setState(() {
userEdited = true;
});
_onFieldChangedForOthers();
},
keyboardType: TextInputType.numberWithOptions(decimal: true),
inputFormatters: [
FilteringTextInputFormatter.allow(RegExp(
@ -1396,7 +1443,12 @@ class _ForexScreenState extends State<ForexScreen> {
child: TextField(
focusNode: focusNodes["_telephone"],
controller: textControllers["_telephone"],
onChanged: (value) => _onFieldChangedForOthers(),
onChanged: (value) {
setState(() {
userEdited = true;
});
_onFieldChangedForOthers();
},
keyboardType: TextInputType.numberWithOptions(decimal: true),
inputFormatters: [
FilteringTextInputFormatter.allow(RegExp(
@ -1536,6 +1588,7 @@ class _ForexScreenState extends State<ForexScreen> {
style: const TextStyle(fontSize: 12),
keyboardType: TextInputType.number,
onChanged: (value) {
// errorMessages["deposit_on_cash"] = "";
_validateCashAmount(
value); // Call validation when text changes
},

View File

@ -610,13 +610,13 @@ class TemplatesListState extends State<TemplatesList> {
fontSize: 13,
fontWeight: FontWeight.w600),
)),
DataColumn(
label: Text(
'Attributes',
style: GoogleFonts.poppins(
fontSize: 13,
fontWeight: FontWeight.w600),
)),
// DataColumn(
// label: Text(
// 'Attributes',
// style: GoogleFonts.poppins(
// fontSize: 13,
// fontWeight: FontWeight.w600),
// )),
DataColumn(
label: Text(
'Actions',
@ -640,12 +640,12 @@ class TemplatesListState extends State<TemplatesList> {
fontSize: 13,
fontFamily: "Inter",
))),
DataCell(Text(
getPlaceholderNames(forex['placeholder']),
style: TextStyle(
fontSize: 13,
fontFamily: "Inter",
))),
// DataCell(Text(
// getPlaceholderNames(forex['placeholder']),
// style: TextStyle(
// fontSize: 13,
// fontFamily: "Inter",
// ))),
DataCell(
// UserActionsMenu(
// user: forex,
@ -661,14 +661,14 @@ class TemplatesListState extends State<TemplatesList> {
// final userId = getUserId(user['user_id']);
// final usersData = await getUserDetails(userId);
//
final forexId = int.tryParse(
final templateId = int.tryParse(
forex['forex_perdiem_id']
.toString());
if (forexId != null) {
print("ForexId -- $forexId");
if (templateId != null) {
print("templateId -- $templateId");
final data = await apiService
.getForexDetailsFind(forexId);
.getTemplateFind(templateId);
print("ForexId -- $data");
} else {
print("Invalid Forex ID");
@ -725,24 +725,24 @@ class TemplatesListState extends State<TemplatesList> {
SizedBox(height: 2),
// Trip Id and Trip Name
Row(
children: [
Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
getPlaceholderNames(
forex['placeholder']),
style: GoogleFonts.poppins(
fontSize: 12,
color: Colors.black87,
fontWeight: FontWeight.w500),
),
],
),
],
),
// Row(
// children: [
// Column(
// crossAxisAlignment:
// CrossAxisAlignment.start,
// children: [
// Text(
// getPlaceholderNames(
// forex['placeholder']),
// style: GoogleFonts.poppins(
// fontSize: 12,
// color: Colors.black87,
// fontWeight: FontWeight.w500),
// ),
// ],
// ),
// ],
// ),
// Actions
// Actions

View File

@ -680,7 +680,8 @@ class CreateNewPlansState extends State<CreateNewPlan> {
}
}
void getSelectedPlanFor() {
Future<void> getSelectedPlanFor() async {
var userTripId;
// if (!mounted) return;
print("getSelectedPlanFor");
setState(() {
@ -689,18 +690,24 @@ class CreateNewPlansState extends State<CreateNewPlan> {
if (selectedIstravelUser!) {
planUsrId = "";
planTravlrId = selectedplanUserId;
userTripId = selectedplanUserId;
} else {
planUsrId = selectedplanUserId;
planTravlrId = "";
userTripId = selectedplanUserId;
}
} else {
print("Is USER ID - $planUsrId ");
planUsrId = selfId;
planTravlrId = "";
_selectedOption = "Option 1";
userTripId = selfId;
}
});
final prefs = await SharedPreferences.getInstance();
await prefs.setString('trip_planned_user', userTripId);
print("USER ID - $planUsrId , TRAVELLER ID - $planTravlrId");
}

View File

@ -2416,7 +2416,7 @@ class TravellerDetailsState extends State<TravellerDetails> {
),
searchFieldProps: TextFieldProps(
decoration: InputDecoration(
hintText: "Search Country...",
hintText: "Search Airline...",
hintStyle: GoogleFonts.poppins(fontSize: 11.5),
contentPadding: EdgeInsets.symmetric(horizontal: 10),
),
@ -2435,7 +2435,7 @@ class TravellerDetailsState extends State<TravellerDetails> {
// Center-align selected item
alignment: Alignment.centerLeft,
child: Text(
selectedItem ?? "Select Country",
selectedItem ?? "Select Airline",
style: TextStyle(fontSize: 12),
),
),

View File

@ -887,6 +887,51 @@ class ApiService {
}
}
Future<Map<String, dynamic>> getTemplateFind(int id) async {
final String apiUrldata = '$apiUrl/api/template/find/$id';
final token = await getToken();
if (token == null) {
throw Exception('Token not found. Please log in.');
}
final response = await http.get(
Uri.parse(apiUrldata),
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
},
);
if (response.statusCode == 200) {
try {
final data = json.decode(response.body);
// print('findout the result');
// print(data.runtimeType);
// print(data);
if (!data.containsKey('data') || data['data'] is! List) {
throw Exception(
"Invalid response format: 'data' field is missing or not a List");
}
final List<Map<String, dynamic>> listData =
List<Map<String, dynamic>>.from(data['data']);
if (listData.isEmpty) {
throw Exception("No department found with ID $id");
}
return listData[0];
} catch (e) {
throw Exception('Error parsing response: $e');
}
} else {
throw Exception('Failed to load department details');
}
}
Future<bool> showCancelConfirmationDialog(
BuildContext context, Color? layoutColor) async {
return await showDialog<bool>(

View File

@ -7,6 +7,11 @@ Future<String?> getToken() async {
return prefs.getString("auth_token");
}
Future<String?> getTripUserId() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getString("trip_planned_user");
}
Future<String?> getLayoutColor() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getString("layout_color");