Commit b35acfea authored by Sai Srinivas's avatar Sai Srinivas
Browse files

local fix

parents ebb11608 2a087139
......@@ -487,11 +487,11 @@ class _FinancedashboardState extends State<Financedashboard> {
},
child: Container(
padding: EdgeInsets.symmetric(
vertical: 5,
vertical: 4.6,
horizontal: 15,
),
margin: EdgeInsets.symmetric(
vertical: 7,
vertical: 5,
horizontal: 5,
),
decoration: BoxDecoration(
......
......@@ -338,6 +338,7 @@ class _PaymentdetailspaymentrequisitionState
fileUrl:
paymentDet
.attachmentDirFilePath!,
downloadEnable: true,
),
),
);
......@@ -604,6 +605,7 @@ class _PaymentdetailspaymentrequisitionState
fileUrl:
paymentDet
.attachmentDirFilePath!,
downloadEnable: true,
),
),
);
......
......@@ -452,6 +452,7 @@ class _PaymentreceiptdetailsState extends State<Paymentreceiptdetails> {
fileUrl:
paymentDet
.attachmentDirFilePath!,
downloadEnable: true,
),
),
);
......
import 'package:dotted_line/dotted_line.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:generp/Utils/custom_snackbar.dart';
import 'package:geocoding/geocoding.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart';
......@@ -246,12 +247,14 @@ class _AttendanceRequestDetailScreenState
"Check In/Out Details",
scaleFactor,
),
if (details.type == "Check In" || details.type == "Check In/Out")
_buildDate_TimeTile(
"Check In Date & Time",
details.date,
details.checkInTime,
scaleFactor,
),
if (details.type == "Check Out" || details.type == "Check In/Out")
_buildDate_TimeTile(
"Check Out Date & Time",
details.date,
......@@ -259,22 +262,28 @@ class _AttendanceRequestDetailScreenState
scaleFactor,
),
if (details.type == "Check In" || details.type == "Check In/Out")
_buildDetailTile(
"Original Check In",
checkInTime,
scaleFactor,
),
if (details.type == "Check Out" || details.type == "Check In/Out")
_buildDetailTile(
"Original Check Out",
"--",
scaleFactor,
),
if (details.type == "Check In" || details.type == "Check In/Out")
_buildDetailTile(
"Original Check In Location",
details.checkInLocation,
scaleFactor,
),
if (details.type == "Check Out" || details.type == "Check In/Out")
_buildDetailTile(
"Original Check Out Location",
details.checkOutLocation,
......@@ -350,16 +359,19 @@ class _AttendanceRequestDetailScreenState
"Other Details",
scaleFactor,
),
if (details.type == "Check In" || details.type == "Check In/Out")
_buildDetailTile(
"Check In Type",
details.checkInType,
scaleFactor,
),
if (details.type == "Check Out" || details.type == "Check In/Out")
_buildDetailTile(
"Check Out Type",
details.chechOutType,
scaleFactor,
),
if (details.type == "Check Out" || details.type == "Check In/Out")
_buildDetailTile(
"Check Out Time",
details.checkOutTime,
......@@ -687,15 +699,11 @@ class _AttendanceRequestDetailScreenState
await onSubmit(remark);
// SnackBar here
Navigator.pop(context);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
"Request submitted successfully",
),
backgroundColor: Colors.green,
behavior: SnackBarBehavior.floating,
),
CustomSnackBar.showSuccess(
context: context,
message: "Request submitted successfully"
);
}
},
child: Container(
......@@ -967,6 +975,7 @@ class _AttendanceRequestDetailScreenState
(context) => Fileviewer(
fileName: filePath ?? "",
fileUrl: filePath ?? "",
downloadEnable: false,
),
),
);
......
......@@ -96,13 +96,13 @@ class _ContactListScreenState extends State<ContactListScreen> {
// Clean phone number
final cleanPhoneNumber = _cleanPhoneNumber(phoneNumber);
// Instead of inserting contact, open system form
// Instead of inserting contact, open system form
final newContact =
Contact()
..name = Name(first: name)
..phones = [Phone(cleanPhoneNumber)];
// 🟢 This opens the phone’s native “Add contact” screen (prefilled)
// This opens the phone’s native “Add contact” screen (prefilled)
await FlutterContacts.openExternalInsert(newContact);
// Hide loading
......
......@@ -242,6 +242,7 @@ class _TourExpensesDetailsScreenState extends State<TourExpensesDetailsScreen> {
(context) => Fileviewer(
fileName: t.imageDirFilePath ?? "",
fileUrl: t.imageDirFilePath ?? "",
downloadEnable: false,
),
),
);
......@@ -301,6 +302,7 @@ class _TourExpensesDetailsScreenState extends State<TourExpensesDetailsScreen> {
(context) => Fileviewer(
fileName: h.imageDirFilePath ?? "",
fileUrl: h.imageDirFilePath ?? "",
downloadEnable: false,
),
),
);
......@@ -359,6 +361,7 @@ class _TourExpensesDetailsScreenState extends State<TourExpensesDetailsScreen> {
(context) => Fileviewer(
fileName: o.imageDirFilePath ?? "",
fileUrl: o.imageDirFilePath ?? "",
downloadEnable: false,
),
),
);
......
......@@ -986,6 +986,7 @@ class _GeneratorPartDetailsScreenState
provider
.partData
.imageDirFilePath!,
downloadEnable: false,
),
),
);
......
......@@ -25,6 +25,7 @@ export 'package:generp/Notifiers/commonProvider/accountDetailsProvider.dart';
export 'package:generp/Notifiers/commonProvider/accountsListProvider.dart';
export 'package:generp/Notifiers/commonProvider/commonPagesProvider.dart';
export 'package:generp/Notifiers/commonProvider/accountLedgerProvider.dart';
export 'package:generp/Notifiers/commonProvider/editCommonAccountProvider.dart';
export 'package:generp/Notifiers/financeProvider/DashboardProvider.dart';
export 'package:generp/Notifiers/financeProvider/RequestionListProvider.dart';
......@@ -56,6 +57,7 @@ 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/crmProvider/CrmNearByGeneratorsProvider.dart';
export 'package:generp/Notifiers/hrmProvider/hrmAccessiblePagesProvider.dart';
export 'package:generp/Notifiers/hrmProvider/attendanceListProvider.dart';
......
......@@ -328,6 +328,7 @@ class _AllpaymentrequesitionlistsbymodesoldState
fileUrl:
requestLists[index]
.attachmentDirFilePath!,
downloadEnable: false,
),
),
);
......
......@@ -316,6 +316,7 @@ class _PaymentlistpaymentrequisitionOldState
fileUrl:
requestLists[index]
.attachmentDirFilePath!,
downloadEnable: false,
),
),
);
......
......@@ -271,6 +271,7 @@ class _PaymentreceiptlistOldState extends State<PaymentreceiptlistOld> {
fileUrl:
requestLists[index]
.attachmentDirFilePath!,
downloadEnable: false,
),
),
);
......
......@@ -1035,6 +1035,7 @@ class _OrdersdetailsbymodesState extends State<Ordersdetailsbymodes> {
fileUrl:
feedbackHistory[lp]
.attachmentDirFilePath!,
downloadEnable: false,
),
),
);
......@@ -1146,6 +1147,7 @@ class _OrdersdetailsbymodesState extends State<Ordersdetailsbymodes> {
fileUrl:
feedbackHistory[lp]
.attachmentDirFilePath!,
downloadEnable: false,
),
),
);
......@@ -1439,6 +1441,7 @@ class _OrdersdetailsbymodesState extends State<Ordersdetailsbymodes> {
fileUrl:
feedbackHistory[lp]
.attachmentDirFilePath!,
downloadEnable: false,
),
),
);
......@@ -2280,6 +2283,7 @@ class _OrdersdetailsbymodesState extends State<Ordersdetailsbymodes> {
fileUrl:
orderDetails
.tpcPaymentAttachmentDirFilePath!,
downloadEnable: false,
),
),
);
......
......@@ -1170,6 +1170,7 @@ class _PaymentdetailsbymodeState extends State<Paymentdetailsbymode> {
fileUrl:
paymentDetails
.attachmentDirFilePath!,
downloadEnable: false,
),
),
);
......
......@@ -329,6 +329,7 @@ class _TpcagentdetailsbymodeState extends State<Tpcagentdetailsbymode> {
fileUrl:
tpcAgentDetails.idProofDirFilePath ??
"",
downloadEnable: false,
),
),
);
......@@ -805,6 +806,7 @@ class _TpcagentdetailsbymodeState extends State<Tpcagentdetailsbymode> {
fileUrl:
tpcAgentDetails
.idProofDirFilePath!,
downloadEnable: false,
),
),
);
......
......@@ -851,6 +851,7 @@ class _TpcagentissuelistdetailsState extends State<Tpcagentissuelistdetails> {
fileUrl:
feedbackHistory[lp]
.attachmentDirFilePath!,
downloadEnable: false,
),
),
);
......@@ -965,6 +966,7 @@ class _TpcagentissuelistdetailsState extends State<Tpcagentissuelistdetails> {
fileUrl:
feedbackHistory[lp]
.attachmentDirFilePath!,
downloadEnable: false,
),
),
);
......@@ -1263,6 +1265,7 @@ class _TpcagentissuelistdetailsState extends State<Tpcagentissuelistdetails> {
fileUrl:
feedbackHistory[lp]
.attachmentDirFilePath!,
downloadEnable: false,
),
),
);
......@@ -1878,6 +1881,7 @@ class _TpcagentissuelistdetailsState extends State<Tpcagentissuelistdetails> {
fileUrl:
orderDetails
.tpcPaymentAttachmentDirFilePath!,
downloadEnable: false,
),
),
);
......
......@@ -442,6 +442,7 @@ class _TpcagentlistbymodeState extends State<Tpcagentlistbymode> {
tpcAgentsLists[index]
.idProofDirFilePath ??
"-",
downloadEnable: false,
),
),
);
......
......@@ -102,6 +102,7 @@ class _FollowupdetailsState extends State<Followupdetails> {
(context) => Fileviewer(
fileName: followups[index].fsrExt!,
fileUrl: "https://erp.gengroup.in/files_genservices/tech_fsr_report/${followups[index].fsrExt!}",
downloadEnable: false,
),
),
);
......
......@@ -12,10 +12,12 @@ import 'package:pin_code_fields/pin_code_fields.dart';
import 'package:provider/provider.dart';
import '../../Models/TechnicianLoadNumbersResponse.dart';
import '../../Notifiers/HomeScreenNotifier.dart';
import '../../Utils/dropdownTheme.dart';
import 'RazorpayQrScreen.dart';
class Paymentdetails extends StatefulWidget {
final accountName, referenceID, name, genId;
final accountName, referenceID, name, genId, complaintType;
const Paymentdetails({
super.key,
......@@ -23,6 +25,7 @@ class Paymentdetails extends StatefulWidget {
required this.name,
required this.genId,
required this.referenceID,
this.complaintType,
});
@override
......@@ -43,6 +46,7 @@ class _PaymentdetailsState extends State<Paymentdetails> {
context,
listen: false,
);
provider.resetErrors();
provider.LoadNumbersAPI(
context,
widget.accountName,
......@@ -267,7 +271,7 @@ class _PaymentdetailsState extends State<Paymentdetails> {
),
],
),
items:
items:// here i have payment option
provider.paymentModeDropDown
.map(
(paymentMode) => DropdownMenuItem<
......@@ -358,39 +362,33 @@ class _PaymentdetailsState extends State<Paymentdetails> {
),
errorWidget(context, provider.selectAmountError),
SizedBox(height: 10),
if (provider.selectPaymentMode?.name?.toLowerCase() != 'qr') ...[
Padding(
padding: const EdgeInsets.only(bottom: 5.0),
child: Text("Reference Number"),
),
Container(
height: 50,
alignment: Alignment.center,
decoration: BoxDecoration(
color: AppColors.text_field_color,
borderRadius: BorderRadius.circular(14),
),
child: Padding(
padding: const EdgeInsets.fromLTRB(
10.0,
0.0,
10,
0,
),
padding: const EdgeInsets.fromLTRB(10.0, 0.0, 10, 0),
child: TextFormField(
controller: provider.Referencecontroller,
keyboardType: TextInputType.text,
onChanged: (value) {
provider.ReferenceError = null;
},
decoration: InputDecoration(
decoration: const InputDecoration(
hintText: "Enter Reference Number",
hintStyle: TextStyle(
fontWeight: FontWeight.w400,
color: Color(0xFFB4BEC0),
fontSize: 14,
),
enabledBorder: InputBorder.none,
focusedBorder: InputBorder.none,
),
......@@ -398,6 +396,9 @@ class _PaymentdetailsState extends State<Paymentdetails> {
),
),
errorWidget(context, provider.ReferenceError),
],
// errorWidget(context, provider.ReferenceError),
SizedBox(height: 10),
InkResponse(
onTap: () {
......@@ -479,11 +480,65 @@ class _PaymentdetailsState extends State<Paymentdetails> {
? null
: () {
HapticFeedback.selectionClick();
// Common validation
if (provider.selectContact == null) {
provider.selectContactError = "Please select phone number";
provider.notifyListeners();
return;
}
if (provider.Amountcontroller.text.isEmpty) {
provider.selectAmountError = "Please enter amount";
provider.notifyListeners();
return;
}
// For QR payment
if (provider.selectPaymentMode?.name?.toLowerCase() == 'qr') {
if (provider.imagePicked == 0 || provider.imagePath == null) {
provider.imageError = "Please upload reference document";
provider.notifyListeners();
return;
}
// All validations passed
var homeProvider = Provider.of<HomescreenNotifier>(
context,
listen: false,
);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => RazorpayQrScreen(
sessionId: homeProvider.session,
empId: homeProvider.empId,
amount: provider.Amountcontroller.text,
refType: widget.complaintType,
refId: widget.referenceID,
),
),
);
} else {
// For non-QR payments
if (provider.Referencecontroller.text.isEmpty) {
provider.ReferenceError = "Please enter reference number";
provider.notifyListeners();
return;
}
if (provider.imagePicked == 0 || provider.imagePath == null) {
provider.imageError = "Please upload reference document";
provider.notifyListeners();
return;
}
provider.PaymentUpdateAPI(
context,
provider.Referencecontroller.text,
provider.Amountcontroller.text,
);
}
},
child: Container(
alignment: Alignment.center,
......@@ -505,10 +560,12 @@ class _PaymentdetailsState extends State<Paymentdetails> {
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
),
)
: const Text(
"Send OTP",
: Text(
provider.selectPaymentMode?.name?.toLowerCase() == 'qr'
? "Show QR"
: "Send OTP",
textAlign: TextAlign.center,
style: TextStyle(
style: const TextStyle(
fontSize: 15,
fontFamily: "JakartaMedium",
color: Colors.white,
......@@ -516,7 +573,9 @@ class _PaymentdetailsState extends State<Paymentdetails> {
),
),
),
)
),
),
),
),
......
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:provider/provider.dart';
import '../../Notifiers/QrProvider.dart';
import '../../Utils/app_colors.dart';
import '../../Utils/commonWidgets.dart';
import '../../Utils/custom_snackbar.dart';
class RazorpayQrScreen extends StatefulWidget {
final String sessionId;
final String empId;
final String amount;
final String refType;
final String refId;
const RazorpayQrScreen({
super.key,
required this.sessionId,
required this.empId,
required this.amount,
required this.refType,
required this.refId,
});
@override
State<RazorpayQrScreen> createState() => _RazorpayQrScreenState();
}
class _RazorpayQrScreenState extends State<RazorpayQrScreen> {
Timer? _statusTimer;
bool _isCancelling = false;
@override
void initState() {
super.initState();
Future.microtask(() async {
final qrProvider = context.read<QrProvider>();
// Step 1: Create QR
await qrProvider.fetchRazorpayQr(
sessionId: widget.sessionId,
empId: widget.empId,
amount: widget.amount,
refType: widget.refType,
refId: widget.refId,
);
// Step 2: Start polling only if QR generated successfully
if (qrProvider.qrResponse?.error == "0" &&
qrProvider.qrResponse?.qrCode != null) {
_startStatusPolling(qrProvider);
}
});
}
void _startStatusPolling(QrProvider provider) {
_statusTimer = Timer.periodic(const Duration(seconds: 5), (timer) async {
if (!mounted) return;
final razorpayOrderId = provider.qrResponse?.qrId;
if (razorpayOrderId == null) return;
final response = await provider.fetchRazorpayUpiQrStatus(
context: context,
sessionId: widget.sessionId,
empId: widget.empId,
razorpayOrderId: razorpayOrderId,
);
if (response != null && response.error == "0") {
// success -> stop timer, notify user and pop
CustomSnackBar.showSuccess(
context: context,
message: "Payment received successfully",
);
timer.cancel();
if (mounted) {
Navigator.pop(context); // close QR screen
Navigator.pop(context); // close previous screen
}
}
});
}
/// Called when user explicitly cancels / presses close.
/// This stops polling and checks the final status once — shows failure snack only if payment not completed.
Future<void> _handleCancel() async {
// Prevent re-entry
if (_isCancelling) return;
_isCancelling = true;
try {
// Stop polling immediately
_statusTimer?.cancel();
_statusTimer = null;
final provider = context.read<QrProvider>();
final razorpayOrderId = provider.qrResponse?.qrId;
if (razorpayOrderId != null) {
// Check status once more before showing failure
final response = await provider.fetchRazorpayUpiQrStatus(
context: context,
sessionId: widget.sessionId,
empId: widget.empId,
razorpayOrderId: razorpayOrderId,
);
// If response == null or not successful -> show warning now (single time)
if (response == null || response.error != "0") {
if (mounted) {
CustomSnackBar.showWarning(
context: context,
message: "⚠️ Payment not yet completed or failed",
);
}
} else {
if (mounted) {
CustomSnackBar.showSuccess(
context: context,
message: "Payment received successfully",
);
}
}
} else {
// No order id — just show failure once
if (mounted) {
CustomSnackBar.showWarning(
context: context,
message: "⚠️ Payment not yet completed or failed",
);
}
}
} catch (e) {
// ignore or log
debugPrint('_handleCancel error: $e');
} finally {
// Finally pop the screen (single pop)
if (mounted) Navigator.pop(context);
// small delay to reset guard if this screen somehow remains mounted longer
// but normally screen is popped so _isCancelling reset isn't necessary
}
}
@override
void dispose() {
_statusTimer?.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async {
await _handleCancel();
return false;
},
child: Scaffold(
appBar: AppBar(
backgroundColor: Color(0xFFFFFFFF),
automaticallyImplyLeading: false,
title: SizedBox(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
InkResponse(
onTap: () => _handleCancel(),
child: SvgPicture.asset(
"assets/svg/appbar_back_button.svg",
height: 25,
),
),
SizedBox(width: 10),
InkResponse(
onTap: () => _handleCancel(),
child: Text(
"Scan QR to Pay",
style: TextStyle(
fontSize: 16,
height: 1.1,
fontFamily: "JakartaSemiBold",
color: AppColors.semi_black,
),
),
),
],
),
),
// shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.vertical(
// bottom: Radius.circular(30), // Adjust the radius as needed
// ),
// ),
),
backgroundColor: Colors.black,
body: SafeArea(
child: Consumer<QrProvider>(
builder: (context, provider, _) {
if (provider.isLoading) {
return _buildLoadingState();
}
if (provider.errorMessage != null) {
return _buildErrorState(provider.errorMessage!);
}
if (provider.qrResponse == null || provider.qrResponse?.qrCode == null) {
return _buildNoQrState();
}
if (provider.secondsLeft == 0) {
Future.delayed(Duration.zero, () {
if (mounted) Navigator.pop(context);
});
return _buildExpiredState();
}
return _buildQrScreen(provider);
},
),
),
),
);
}
Widget _buildLoadingState() {
return Container(
color: Colors.black,
child: const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
),
SizedBox(height: 20),
Text(
"Generating QR Code...",
style: TextStyle(
color: Colors.white,
fontSize: 16,
),
),
],
),
),
);
}
Widget _buildErrorState(String errorMessage) {
return Container(
color: Colors.black,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(
Icons.error_outline,
size: 60,
color: Colors.redAccent,
),
const SizedBox(height: 20),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 40),
child: Text(
errorMessage,
style: const TextStyle(
color: Colors.white,
fontSize: 16,
),
textAlign: TextAlign.center,
),
),
const SizedBox(height: 30),
_buildBackButton(),
],
),
),
);
}
Widget _buildNoQrState() {
return Container(
color: Colors.black,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(
Icons.qr_code_2,
size: 60,
color: Colors.grey,
),
const SizedBox(height: 20),
const Text(
"No QR Code Found",
style: TextStyle(
color: Colors.white,
fontSize: 18,
),
),
const SizedBox(height: 30),
_buildBackButton(),
],
),
),
);
}
Widget _buildExpiredState() {
return Container(
color: Colors.black,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(
Icons.timer_off_rounded,
size: 60,
color: Colors.redAccent,
),
const SizedBox(height: 20),
const Text(
"QR Expired",
style: TextStyle(
fontSize: 20,
color: Colors.redAccent,
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 10),
const Text(
"Returning to previous screen...",
style: TextStyle(
color: Colors.white70,
fontSize: 14,
),
),
],
),
),
);
}
Widget _buildQrScreen(QrProvider provider) {
final screenHeight = MediaQuery.of(context).size.height;
final screenWidth = MediaQuery.of(context).size.width;
return Stack(
children: [
// Full Screen QR Code Background - Preserving all text
Positioned.fill(
child: Image.network(
provider.qrResponse!.qrCode!,
fit: BoxFit.contain, // Use contain to preserve all content
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
return Container(
color: Colors.black,
child: const Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
),
),
);
},
errorBuilder: (context, error, stackTrace) {
return Container(
color: Colors.black,
child: const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.error, size: 60, color: Colors.red),
SizedBox(height: 16),
Text(
"Failed to load QR code",
style: TextStyle(color: Colors.white, fontSize: 16),
),
],
),
),
);
},
),
),
// Top Bar - Timer and Close
Positioned(
top: 0,
left: 0,
right: 0,
child: Container(
height: 80,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.black.withOpacity(0.8),
Colors.transparent,
],
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// Timer on left
Container(
margin: const EdgeInsets.only(left: 20, top: 40),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.7),
borderRadius: BorderRadius.circular(20),
border: Border.all(
color: Colors.orange,
width: 1,
),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(
Icons.timer,
color: Colors.orange,
size: 16,
),
const SizedBox(width: 6),
Text(
"${provider.secondsLeft}s",
style: const TextStyle(
color: Colors.white,
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
],
),
),
// Close button on right
Container(
margin: const EdgeInsets.only(right: 20, top: 40),
child: CircleAvatar(
radius: 20,
backgroundColor: Colors.transparent,
child: IconButton(
icon: const Icon(Icons.close, size: 18),
color: Colors.transparent,
onPressed: () => _handleCancel(),
),
),
),
],
),
),
),
// Bottom Section - Amount and Action Buttons
// Positioned at the very bottom to avoid covering QR content
Positioned(
bottom: 0,
left: 0,
right: 0,
child: Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.bottomCenter,
end: Alignment.topCenter,
colors: [
Colors.black.withOpacity(0.9),
Colors.black.withOpacity(0.6),
Colors.transparent,
],
),
),
child: Column(
children: [
// Amount Display
// Container(
// width: double.infinity,
// padding: const EdgeInsets.all(16),
// decoration: BoxDecoration(
// color: AppColors.app_blue.withOpacity(0.9),
// borderRadius: BorderRadius.circular(12),
// boxShadow: [
// BoxShadow(
// color: Colors.black.withOpacity(0.3),
// blurRadius: 10,
// offset: const Offset(0, 4),
// ),
// ],
// ),
// child: Column(
// children: [
// const Text(
// "Amount to Pay",
// style: TextStyle(
// color: Colors.white70,
// fontSize: 14,
// fontWeight: FontWeight.w500,
// ),
// ),
// const SizedBox(height: 8),
// Text(
// "₹${widget.amount}",
// style: const TextStyle(
// fontSize: 28,
// fontWeight: FontWeight.bold,
// color: Colors.white,
// ),
// ),
// ],
// ),
// ),
const SizedBox(height: 20),
// Action Buttons
Row(
children: [
// Cancel Button
Expanded(
child: ElevatedButton.icon(
onPressed: () => _handleCancel(),
icon: const Icon(Icons.close, size: 18),
label: const Text("Cancel"),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red.withOpacity(0.9),
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
padding: const EdgeInsets.symmetric(vertical: 12),
elevation: 3,
),
),
),
const SizedBox(width: 12),
// Instructions Button
Expanded(
child: ElevatedButton.icon(
onPressed: _showInstructions,
icon: const Icon(Icons.help_outline, size: 18),
label: const Text("Help"),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.grey.withOpacity(0.8),
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
padding: const EdgeInsets.symmetric(vertical: 12),
elevation: 3,
),
),
),
],
),
],
),
),
),
// Small status indicator at the top center (minimal space usage)
Positioned(
top: 40,
left: 0,
right: 0,
child: Center(
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
decoration: BoxDecoration(
color: Colors.green.withOpacity(0.9),
borderRadius: BorderRadius.circular(12),
),
child: const Text(
"READY TO SCAN",
style: TextStyle(
color: Colors.white,
fontSize: 12,
fontWeight: FontWeight.w600,
letterSpacing: 1.0,
),
),
),
),
),
],
);
}
void _showInstructions() {
showModalBottomSheet(
context: context,
backgroundColor: Colors.transparent,
builder: (context) => Container(
decoration: BoxDecoration(
color: AppColors.scaffold_bg_color,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(20),
topRight: Radius.circular(20),
),
),
padding: const EdgeInsets.all(20),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
width: 40,
height: 4,
decoration: BoxDecoration(
color: Colors.grey,
borderRadius: BorderRadius.circular(2),
),
),
const SizedBox(height: 20),
const Text(
"How to Pay",
style: TextStyle(
color: Colors.blue,
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 16),
_buildInstructionStep("1", "Open any UPI app on your phone"),
_buildInstructionStep("2", "Tap on 'Scan QR Code'"),
_buildInstructionStep("3", "Point your camera at this QR code"),
_buildInstructionStep("4", "Enter UPI PIN to complete payment"),
const SizedBox(height: 20),
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: () => Navigator.pop(context),
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.app_blue,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
padding: const EdgeInsets.symmetric(vertical: 12),
),
child: const Text("Got It"),
),
),
const SizedBox(height: 10),
],
),
),
);
}
Widget _buildInstructionStep(String number, String text) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: 24,
height: 24,
decoration: BoxDecoration(
color: AppColors.app_blue,
shape: BoxShape.circle,
),
child: Center(
child: Text(
number,
style: const TextStyle(
color: Colors.white,
fontSize: 12,
fontWeight: FontWeight.bold,
),
),
),
),
const SizedBox(width: 12),
Expanded(
child: Text(
text,
style: const TextStyle(
color: Colors.black87,
fontSize: 14,
),
),
),
],
),
);
}
Widget _buildBackButton() {
return ElevatedButton.icon(
onPressed: () => Navigator.pop(context),
icon: const Icon(Icons.arrow_back),
label: const Text("Go Back"),
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.app_blue,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 24),
),
);
}
}
\ No newline at end of file
......@@ -7,9 +7,11 @@ import 'package:flutter_svg/svg.dart';
import 'package:generp/Notifiers/VisitDetailsProvider.dart';
import 'package:generp/Utils/app_colors.dart';
import 'package:generp/Utils/commonWidgets.dart';
import 'package:generp/screens/serviceEngineer/RazorpayQrScreen.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart';
import '../../Notifiers/HomeScreenNotifier.dart';
import '../../Utils/commonServices.dart';
import '../finance/FileViewer.dart';
import 'Followupdetails.dart';
......@@ -46,6 +48,7 @@ class _VisitdetailsState extends State<Visitdetails> {
visitdetails.showMoreDetails = false;
visitdetails.LoadVisitDetailsAPI(context, widget.complaintID);
visitdetails.LoadFollowupListAPI(context, widget.complaintID);
visitdetails.serviceComplaintBillList(context, widget.complaintID);
});
}
......@@ -90,6 +93,7 @@ class _VisitdetailsState extends State<Visitdetails> {
var generatorDetails = provider.generatorDetails;
var complaintDetails = provider.complaintDetailsNew;
var followups = provider.followUpList;
var complaintBillList = provider.complaintList;
return WillPopScope(
onWillPop: () => onBackPressed(context),
child: SafeArea(
......@@ -942,6 +946,7 @@ class _VisitdetailsState extends State<Visitdetails> {
.fsrExt!,
fileUrl:
"https://erp.gengroup.in/files_genservices/tech_fsr_report/${followups[lp].fsrExt!}",
downloadEnable: false,
),
),
);
......@@ -993,6 +998,129 @@ class _VisitdetailsState extends State<Visitdetails> {
),
),
],
if (complaintBillList.isNotEmpty) ...[
Container(
padding: EdgeInsets.symmetric(
horizontal: 10,
vertical: 5,
),
child: Text(
"Complaint Bill List",
style: TextStyle(
color: Color(0xFF818181),
fontFamily: "JakartaMedium",
),
),
),
SizedBox(
width: double.infinity,
height: 350,
child: ListView.builder(
padding: EdgeInsets.all(12),
itemCount: complaintBillList!.length,
itemBuilder: (context, index) {
final item = complaintBillList![index];
return Card(
margin: EdgeInsets.only(bottom: 12),
elevation: 0,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: Padding(
padding: const EdgeInsets.all(14),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// _detailRow("Bill ID", item.billId),
// _detailRow("Total Amount", item.totalAmount),
// _detailRow("Raw Amount", item.rawAmount),
// _detailRow("Narration", item.narration),
// _detailRow("Bill Date", item.billDate),
// _detailRow("Due Date", item.dueDate),
// _detailRow("Bill Paid", item.billPaid == "1" ? "Yes" : "No"),
Row(
children: [
// 👤 Avatar
Container(
height: 40,
width: 40,
decoration: const BoxDecoration(
color: Color(0xFFE6F6FF),
shape: BoxShape.circle,
),
clipBehavior: Clip.antiAlias,
child: SvgPicture.asset(
"assets/svg/compliant_list_ic.svg",
),
),
const SizedBox(width: 12),
// 📝 Info
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
item.narration ?? "-",
style: const TextStyle(
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w400,
fontSize: 14,
color: Colors.black87,
),
),
const SizedBox(height: 3),
Text(
item.dueDate ?? "-",
style: TextStyle(
fontFamily: "JakartaRegular",
fontSize: 12,
fontWeight: FontWeight.w400,
color: Colors.grey.shade600,
height: 1.4,
),
),
],
),
),
// Call
if (item.billPaid == "0")
InkResponse(
onTap: (){
var homeProvider = Provider.of<HomescreenNotifier>(
context,
listen: false,
);
Navigator.push(
context,
MaterialPageRoute(builder: (context) => RazorpayQrScreen(
sessionId: homeProvider.session,
empId: homeProvider.empId,
amount: item.rawAmount.toString(),
refType: "Bill",
refId: item.billId.toString()
))
);
},
child: Text("Pay Now", style: TextStyle(
fontSize: 14,
color: AppColors.app_blue,
),),
)
],
),
],
),
),
);
},
)
),
],
SizedBox(height: 75),
],
),
......@@ -1022,6 +1150,7 @@ class _VisitdetailsState extends State<Visitdetails> {
provider
.complaintDetailsNew
.complaintId,
complaintType: provider.complaintDetailsNew.complaintType,
),
),
);
......@@ -1213,6 +1342,28 @@ class _VisitdetailsState extends State<Visitdetails> {
);
}
Widget _detailRow(String title, String? value) {
return Padding(
padding: const EdgeInsets.only(bottom: 8),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"$title: ",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14),
),
Expanded(
child: Text(
value ?? "--",
style: TextStyle(fontSize: 14),
),
),
],
),
);
}
Widget _scaffold(BuildContext context) {
return Consumer<Visitdetailsprovider>(
builder: (context, provider, child) {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment