"assets/git@183.82.99.133:saisrinivas/gen_services.git" did not exist on "fc0fa1ba4f9dbaccaba3cef06ef44af02a2f4df5"
Commit 97cb09f8 authored by Sai Srinivas's avatar Sai Srinivas
Browse files

Profile and some more changes

parent b8c2cae9
...@@ -5,6 +5,7 @@ import 'package:flutter_svg/flutter_svg.dart'; ...@@ -5,6 +5,7 @@ import 'package:flutter_svg/flutter_svg.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../../Notifier/HelpAndEnquiryProvider.dart'; import '../../Notifier/HelpAndEnquiryProvider.dart';
import '../../Utility/AppColors.dart'; import '../../Utility/AppColors.dart';
import '../../Utility/FullScreenImageViewer.dart';
import '../../Utility/Reusablewidgets.dart'; import '../../Utility/Reusablewidgets.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
...@@ -84,17 +85,17 @@ class _TicketChatScreenState extends State<TicketChatScreen> { ...@@ -84,17 +85,17 @@ class _TicketChatScreenState extends State<TicketChatScreen> {
const Spacer(), const Spacer(),
Container( Container(
padding: padding:
const EdgeInsets.symmetric(horizontal: 10, vertical: 6), const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration( decoration: BoxDecoration(
color: status ? Colors.green.shade50 : Colors.red.shade50, color: status ? Color(0xFFD7FFD2) : Color(0xFFFFEBEB),
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(6),
), ),
child: Text( child: Text(
widget.status, widget.status,
style: TextStyle( style: TextStyle(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
color: status ? Colors.green : Colors.red, color: status ? Colors.black87 : Colors.red,
), ),
), ),
), ),
...@@ -134,6 +135,7 @@ class _TicketChatScreenState extends State<TicketChatScreen> { ...@@ -134,6 +135,7 @@ class _TicketChatScreenState extends State<TicketChatScreen> {
), ),
), ),
), ),
if(widget.status != "Closed")
Container( Container(
width: double.infinity, width: double.infinity,
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
...@@ -243,17 +245,6 @@ class _TicketChatScreenState extends State<TicketChatScreen> { ...@@ -243,17 +245,6 @@ class _TicketChatScreenState extends State<TicketChatScreen> {
), ),
), ),
const SizedBox(height: 6), const SizedBox(height: 6),
Text(
message,
style: const TextStyle(
fontSize: 14,
fontStyle: FontStyle.normal,
fontWeight: FontWeight.w400,
color: Colors.black87,
height: 1.5,
),
),
const SizedBox(height: 8),
// 🔹 Image container section // 🔹 Image container section
if (images.isNotEmpty) if (images.isNotEmpty)
...@@ -269,21 +260,36 @@ class _TicketChatScreenState extends State<TicketChatScreen> { ...@@ -269,21 +260,36 @@ class _TicketChatScreenState extends State<TicketChatScreen> {
children: [ children: [
ClipRRect( ClipRRect(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
child: Image.network( child: GestureDetector(
images[index], onTap: () {
width: 100, Navigator.push(
height: 80, context,
fit: BoxFit.cover, MaterialPageRoute(
errorBuilder: (context, error, stackTrace) { builder: (_) => FullScreenImageViewer(
return Container( imageUrl: images[index],
width: 100, ),
height: 80, ),
color: Colors.grey.shade200,
child: const Icon(Icons.broken_image,
color: Colors.grey),
); );
}, },
child: ClipRRect(
borderRadius: BorderRadius.circular(12),
child: Image.network(
images[index],
width: 100,
height: 80,
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) {
return Container(
width: 100,
height: 80,
color: Colors.grey.shade200,
child: const Icon(Icons.broken_image, color: Colors.grey),
);
},
),
),
), ),
), ),
if (imageName.length > index) if (imageName.length > index)
Padding( Padding(
...@@ -304,6 +310,19 @@ class _TicketChatScreenState extends State<TicketChatScreen> { ...@@ -304,6 +310,19 @@ class _TicketChatScreenState extends State<TicketChatScreen> {
), ),
), ),
Text(
message,
style: const TextStyle(
fontSize: 14,
fontStyle: FontStyle.normal,
fontWeight: FontWeight.w400,
color: Colors.black87,
height: 1.5,
),
),
const SizedBox(height: 8),
// Divider line // Divider line
Container( Container(
height: 1, height: 1,
......
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:gen_rentals/Screens/BillDetailListScreen.dart'; import 'package:gen_rentals/Screens/BillScreens/BillDetailListScreen.dart';
import 'package:gen_rentals/Utility/Reusablewidgets.dart'; import 'package:gen_rentals/Utility/Reusablewidgets.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../Models/SubscribeOrderDetailsResponse.dart'; import '../Models/SubscribeOrderDetailsResponse.dart';
...@@ -108,9 +108,14 @@ class _ProductsDetailScreenState extends State<ProductsDetailScreen> { ...@@ -108,9 +108,14 @@ class _ProductsDetailScreenState extends State<ProductsDetailScreen> {
width: double.infinity, width: double.infinity,
child: ElevatedButton( child: ElevatedButton(
onPressed: () { onPressed: () {
final order = provider.orderDetails!;
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute(builder: (context) => BillDetailListScreen()) MaterialPageRoute(builder: (context) => BillDetailListScreen(
sessionId: widget.sessionId,
orderId: order.orderid.toString(),
accId: widget.accId,
))
); );
// Handle view bill action // Handle view bill action
FocusScope.of(context).unfocus(); FocusScope.of(context).unfocus();
...@@ -193,113 +198,129 @@ class _ProductsDetailScreenState extends State<ProductsDetailScreen> { ...@@ -193,113 +198,129 @@ class _ProductsDetailScreenState extends State<ProductsDetailScreen> {
children: [ children: [
SizedBox(height: 1,), SizedBox(height: 1,),
// Order header (not in card) // Order header (not in card)
Container( Padding(
width: double.infinity, padding: const EdgeInsets.all(14),
margin: const EdgeInsets.all(0), child: Container(
padding: const EdgeInsets.all(0), width: double.infinity,
decoration: BoxDecoration( margin: const EdgeInsets.all(0),
borderRadius: BorderRadius.circular(2), padding: EdgeInsets.symmetric(horizontal: 0, vertical: 16),
gradient: LinearGradient( decoration: BoxDecoration(
begin: Alignment.topCenter, borderRadius: BorderRadius.circular(22),
end: Alignment.bottomCenter, gradient: LinearGradient(
colors: [ begin: Alignment.topCenter,
Color(0xDDFFFFFF), end: Alignment.bottomCenter,
Color(0xAAFFFFFF), colors: [
Color(0x88FFFFFF), Color(0xFFFFFFFF),
Color(0x66FFFFFF), Color(0xAAFFFFFF),
Color(0x44FFFFFF), Color(0xFFFFFFFF),
Color(0x11FFFFFF),
] ]
) )
), ),
child: Padding(
padding: const EdgeInsets.all(18),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Row( SizedBox(height: 2,),
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
"Order ID ",
style: TextStyle(
fontSize: 24,
fontFamily: "Poppins",
fontWeight: FontWeight.w400,
color: Colors.black87,
),
),
Text(
"#${order.orderNum ?? order.orderid ?? 'N/A'}",
style: TextStyle(
fontSize: 24,
fontFamily: "Poppins",
fontWeight: FontWeight.w400,
color: AppColors.amountText,
),
),
],
),
const SizedBox(height: 12),
//
Text(
"Duration",
style: TextStyle(
fontSize: 12,
fontFamily: "Poppins",
fontWeight: FontWeight.w400,
color: Colors.black87,
),
),
const SizedBox(height: 2),
Text(
"${order.fromDate} - ${order.toDate ?? 'Duration unavailable'}",
style: TextStyle(
fontSize: 12,
fontFamily: "Poppins",
fontWeight: FontWeight.w400,
color: AppColors.subtitleText,
),
),
const SizedBox(height: 12),
Container( Container(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 6),
horizontal: 10, vertical: 6),
decoration: BoxDecoration( decoration: BoxDecoration(
gradient: _getGradientByColor(order.expiringInColor), gradient: LinearGradient(
borderRadius: BorderRadius.circular(8), begin: Alignment.topLeft,
end: Alignment.topRight,
colors: [
Color(0xffD4EFFF),
// Color(0xFFFFFFFF),
Color(0xFFFFFFFF),
]
)
), ),
child: Text( child: Row(
order.expiringText ?? 'Expiring info not available', mainAxisAlignment: MainAxisAlignment.spaceBetween,
style: const TextStyle( children: [
fontSize: 12, Column(
fontFamily: "Poppins", children: [
fontWeight: FontWeight.w400, Text(
color: Colors.black87, "Order ID #${order.orderNum ?? order.orderid ?? 'N/A'}",
), style: TextStyle(
fontSize: 14,
fontFamily: "Poppins",
fontWeight: FontWeight.w400,
color: AppColors.amountText,
),
),
SizedBox(height: 4,),
Text(
"${order.fromDate} - ${order.toDate ?? 'Duration unavailable'}",
style: TextStyle(
fontSize: 12,
fontFamily: "Poppins",
fontWeight: FontWeight.w400,
color: AppColors.subtitleText,
),
),
],
),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 10, vertical: 6),
decoration: BoxDecoration(
gradient: _getGradientByColor(order.expiringInColor),
borderRadius: BorderRadius.circular(8),
),
child: Text(
order.expiringText ?? 'Expiring info not available',
style: const TextStyle(
fontSize: 12,
fontFamily: "Poppins",
fontWeight: FontWeight.w400,
color: Colors.black87,
),
),
),
],
), ),
), ),
const SizedBox(height: 14), const SizedBox(height: 12),
// //
Text( Padding(
order.accName ?? ' A/c not available', padding: EdgeInsets.symmetric(horizontal: 14, vertical: 4),
style: TextStyle( child: Column(
fontSize: 12, crossAxisAlignment: CrossAxisAlignment.start,
fontFamily: "Poppins", children: [
fontWeight: FontWeight.w400, Text(
color: Colors.black87, "Address",
), style: TextStyle(
), fontSize: 12,
const SizedBox(height: 10), fontFamily: "Poppins",
Text( fontWeight: FontWeight.w400,
order.adress ?? "Address unavailable", color: Colors.black87,
style: TextStyle( ),
fontSize: 12, ),
fontFamily: "Poppins", const SizedBox(height: 8),
fontWeight: FontWeight.w400, //
color: AppColors.subtitleText, Text(
order.accName ?? ' A/c not available',
style: TextStyle(
fontSize: 12,
fontFamily: "Poppins",
fontWeight: FontWeight.w400,
color: Colors.black87,
),
),
const SizedBox(height: 10),
Text(
order.adress ?? "Address unavailable",
style: TextStyle(
fontSize: 12,
fontFamily: "Poppins",
fontWeight: FontWeight.w400,
color: AppColors.subtitleText,
),
textAlign: TextAlign.start,
),
],
), ),
textAlign: TextAlign.center,
), ),
], ],
), ),
...@@ -539,14 +560,14 @@ class _ProductsDetailScreenState extends State<ProductsDetailScreen> { ...@@ -539,14 +560,14 @@ class _ProductsDetailScreenState extends State<ProductsDetailScreen> {
switch (color) { switch (color) {
case "Red": case "Red":
return const LinearGradient( return const LinearGradient(
colors: [Color(0xFFFFE0E0), Color(0xFFFFC0C0)], colors: [Color(0xFFFFEBEB), Color(0xFFFFEBEB)],
begin: Alignment.topLeft, begin: Alignment.topLeft,
end: Alignment.bottomRight, end: Alignment.bottomRight,
); );
case "Green": case "Green":
default: default:
return const LinearGradient( return const LinearGradient(
colors: [Color(0xFFE9FFDD), Color(0xFFB5FFD1)], colors: [Color(0xFFE5FFE2), Color(0xFFE5FFE2)],
begin: Alignment.topLeft, begin: Alignment.topLeft,
end: Alignment.bottomRight, end: Alignment.bottomRight,
); );
......
...@@ -7,6 +7,10 @@ import '../../Notifier/HelpAndEnquiryProvider.dart'; ...@@ -7,6 +7,10 @@ import '../../Notifier/HelpAndEnquiryProvider.dart';
import '../../Utility/AppColors.dart'; import '../../Utility/AppColors.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../Notifier/RentalContactProvider .dart';
import '../Utility/SharedpreferencesService.dart';
import 'authScreen/LoginScreen.dart';
class ProfileScreen extends StatefulWidget { class ProfileScreen extends StatefulWidget {
final String sessionId; final String sessionId;
...@@ -37,99 +41,595 @@ class _ProfileScreenState extends State<ProfileScreen> { ...@@ -37,99 +41,595 @@ class _ProfileScreenState extends State<ProfileScreen> {
}); });
} }
// ✅ (unchanged) Future<void> onLogout(BuildContext context) async {
final List<Map<String, dynamic>> createNewTickets = [ final provider = Provider.of<RentalProvider>(context, listen: false);
{
'title': 'Payment Issues',
'description': 'Get help with payment related problems',
'icon': "assets/svg/rupee_coin_ic.svg",
'color': Color(0xFFFFEFBE),
},
{
'title': 'Bill Related Issues',
'description': 'Resolve bill and invoice matters',
'icon': "assets/svg/know_pay.svg",
'color': Color(0xFFCEF9FF),
},
{
'title': 'Other Issues',
'description': 'Any other support you need',
'icon': 'assets/svg/help_ic.svg',
'color': Color(0xFFE4E5FF),
},
];
@override // 🧭 Step 1: Ask confirmation
Widget build(BuildContext context) { final confirm = await showDialog<bool>(
return SafeArea( context: context,
top: false, builder: (context) {
child: Scaffold( return Dialog(
backgroundColor: AppColors.backgroundRegular,
appBar: AppBar(
automaticallyImplyLeading: false,
backgroundColor: Colors.white, backgroundColor: Colors.white,
title: Row( shape: RoundedRectangleBorder(
children: [ borderRadius: BorderRadius.circular(20),
InkResponse( ),
onTap: () => Navigator.pop(context, true), elevation: 0,
child: SvgPicture.asset( child: Container(
"assets/svg/continue_left_ic.svg", padding: const EdgeInsets.all(24),
height: 30, decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 20,
offset: const Offset(0, 4),
),
],
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// Icon
Container(
width: 60,
height: 60,
decoration: BoxDecoration(
color: AppColors.buttonColor.withOpacity(0.1),
shape: BoxShape.circle,
),
child: Icon(
Icons.logout_rounded,
color: AppColors.buttonColor,
size: 30,
),
), ),
const SizedBox(height: 16),
// Title
Text(
"Logout",
style: TextStyle(
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w700,
fontSize: 20,
color: Colors.black87,
),
),
const SizedBox(height: 8),
// Subtitle
Text(
"Are you sure you want to logout?",
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: "Plus Jakarta Sans",
fontSize: 14,
fontWeight: FontWeight.w400,
color: Colors.grey[600],
height: 1.4,
),
),
const SizedBox(height: 24),
// Buttons Row
Row(
children: [
// Cancel Button
Expanded(
child: OutlinedButton(
onPressed: () => Navigator.pop(context, false),
style: OutlinedButton.styleFrom(
backgroundColor: Colors.transparent,
foregroundColor: Colors.grey[600],
side: BorderSide(
color: Colors.grey[300]!,
width: 1.5,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
padding: const EdgeInsets.symmetric(vertical: 14),
elevation: 0,
),
child: Text(
"Cancel",
style: TextStyle(
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w600,
fontSize: 14,
color: Colors.grey[700],
),
),
),
),
const SizedBox(width: 12),
// Logout Button
Expanded(
child: ElevatedButton(
onPressed: () => Navigator.pop(context, true),
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.buttonColor,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
padding: const EdgeInsets.symmetric(vertical: 14),
elevation: 0,
shadowColor: Colors.transparent,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Logout",
style: TextStyle(
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w600,
fontSize: 14,
color: Colors.white,
),
),
],
),
),
),
],
),
],
),
),
);
},
);
// Step 2: If user cancelled, just return
if (confirm != true) return;
// Step 3: Show loading indicator
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => Center(
child: Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 10,
offset: const Offset(0, 4),
),
],
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
CircularProgressIndicator(
color: AppColors.buttonColor,
strokeWidth: 3,
), ),
const SizedBox(width: 10), const SizedBox(height: 16),
const Text( Text(
"Profile", "Logging out...",
style: TextStyle( style: TextStyle(
fontSize: 16,
fontFamily: "Plus Jakarta Sans", fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w600, fontWeight: FontWeight.w500,
color: Colors.black87, color: Colors.grey[700],
), ),
), ),
], ],
), ),
), ),
),
);
try {
// Step 4: Call provider to logout
final deviceDetails = {
"device_name": "Android Device",
"os_version": "14",
};
await provider.logout(
context,
widget.accId,
widget.sessionId,
deviceDetails,
);
// ✅ Provider Consumer used here // Step 5: Clear SharedPreferences
body: Consumer<HelpAndEnquiryProvider>( final prefs = SharedPreferencesService.instance;
builder: (context, provider, _) { await prefs.clearPreferences();
if (provider.isLoading) {
return const Center(child: CircularProgressIndicator()); // Close loading dialog
} if (context.mounted) Navigator.pop(context);
if (provider.errorMessage != null) { // Step 6: Navigate to login screen with success feedback
return Center( if (context.mounted) {
child: Text(provider.errorMessage!, // Show success message briefly
style: const TextStyle(color: Colors.red)), ScaffoldMessenger.of(context).showSnackBar(
); SnackBar(
} backgroundColor: AppColors.buttonColor,
content: Row(
final ticketData = provider.ticketListResponse?.tickets; children: [
final processingTickets = ticketData?.inProgress ?? []; Icon(Icons.check_circle, color: Colors.white, size: 20),
final closedTickets = ticketData?.closed ?? []; const SizedBox(width: 8),
Text(
return SingleChildScrollView( "Logged out successfully",
padding: const EdgeInsets.all(16), style: TextStyle(
child: Column( fontFamily: "Plus Jakarta Sans",
crossAxisAlignment: CrossAxisAlignment.start, fontWeight: FontWeight.w500,
children: [
// Create New Ticket Section
const SectionHeading(
title: 'User Profile',
padding: EdgeInsets.symmetric(horizontal: 2, vertical: 4),
), ),
),
],
),
duration: const Duration(seconds: 2),
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
);
], // Navigate after brief delay
), await Future.delayed(const Duration(milliseconds: 1500));
);
}, if (context.mounted) {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (_) => const LoginScreen()),
(route) => false,
);
}
}
} catch (e) {
// Close loading dialog
if (context.mounted) Navigator.pop(context);
// Show error message
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
backgroundColor: Colors.redAccent,
content: Row(
children: [
Icon(Icons.error_outline, color: Colors.white, size: 20),
const SizedBox(width: 8),
Text(
"Logout failed. Please try again.",
style: TextStyle(
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w500,
),
),
],
),
duration: const Duration(seconds: 3),
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
);
}
}
}
@override
Widget build(BuildContext context) {
double screenWidth = MediaQuery.of(context).size.width;
double screenHeight = MediaQuery.of(context).size.height;
double bottomPadding = MediaQuery.of(context).padding.bottom;
return SafeArea(
top: false,
child: Scaffold(
body: Container(
color: const Color(0xFFF3F3F3),
height: screenHeight,
child: SingleChildScrollView(
child: Column(
children: [
// Top background image section
Stack(
children: [
// Background image
Container(
width: double.infinity,
decoration: BoxDecoration(
gradient: const LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.white54,
Color(0xFFF3F3F3),
],
),
),
child: Image.asset(
'assets/images/sky_blue_bg.jpg',
width: double.infinity,
height: 400,
fit: BoxFit.cover,
),
),
// Content overlay
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Header with profile
Container(
width: double.infinity,
height: 400,
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Color(0x22FFFFFF),
Color(0x33FFFFFF),
Color(0x88FFFFFF),
Color(0xFFF3F3F3),
Color(0xFFF3F3F3),
],
),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
const SizedBox(height: 0),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
const SizedBox(width: 20),
// Profile icon
InkResponse(
onTap: () {
Navigator.pop(context);
},
child: Container(
width: 32,
height: 32,
decoration: BoxDecoration(
color: Colors.transparent,
),
child: SvgPicture.asset(
"assets/svg/continue_left_ic.svg",
fit: BoxFit.contain,
),
),
),
SizedBox(width: 5,),
Text(
"Profile",
style: TextStyle(
fontSize: 16,
fontFamily: "Poppins",
fontWeight: FontWeight.w400,
color: AppColors.normalText,
),
),
],
),
SizedBox(height: 120,),
Container(
height: 140,
width: 140,
decoration: const BoxDecoration(
color: Color(0xFFE6F6FF),
shape: BoxShape.circle,
),
clipBehavior: Clip.antiAlias,
child: (widget.accId.isNotEmpty)
? Image.network(
widget.accId,
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) {
return const Icon(Icons.person, color: Color(0xFF2d2d2d), size: 100,);
},
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
return const Center(
child: CircularProgressIndicator(strokeWidth: 2));
},
)
: CircleAvatar(
radius: 80,
backgroundColor: Colors.blue.shade100,
child: SvgPicture.asset(
"assets/svg/person_ic.svg",
fit: BoxFit.contain,
),
),
),
const SizedBox(height: 10),
Text(
"Blink Commerce PVT. LTD - TN",
style: TextStyle(
fontSize: 16,
fontFamily: "Poppins",
fontWeight: FontWeight.w400,
color: AppColors.normalText,
),
),
const SizedBox(height: 10),
],
),
),
// Main content section
Container(
margin: const EdgeInsets.all(14),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20),
decoration: BoxDecoration(
color: Colors.transparent,
borderRadius: BorderRadius.circular(18),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// 📞 Phone
_infoItem(
iconPath: "assets/svg/phone_ic.svg",
title: "Phone No.",
value: "+91 9639919XX",
iconBgColor: const Color(0xFFDFF6E3),
),
const SizedBox(height: 16),
// 📧 Email
_infoItem(
iconPath: "assets/svg/email_ic.svg",
title: "Email ID",
value: "xyz@gmail.com",
iconBgColor: const Color(0xFFDDEEFF),
),
const SizedBox(height: 16),
// 📍 Address
_infoItem(
iconPath: "assets/svg/location_ic.svg", title: "Address",
value: "No.1/398 O.M. Road, Navallur, Chennai - 600130",
iconBgColor: const Color(0xFFFFE9E9),
),
const SizedBox(height: 20),
// 🌏 State / Sub locality
Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_twoColumnItem("State", "Tamil Nadu"),
_twoColumnItem("Sub Locality", "Chennai"),
],
),
),
const SizedBox(height: 30),
// 🚪 Logout Button
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: () => onLogout(context),
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.amountText,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50),
),
padding: const EdgeInsets.symmetric(vertical: 14),
),
child: const Text(
"Logout",
style: TextStyle(
fontSize: 16,
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w600,
color: Colors.white,
),
),
),
),
],
),
)
],
),
],
),
],
),
),
), ),
), ),
); );
} }
Widget _infoItem({
required String iconPath,
required String title,
required String value,
required Color iconBgColor,
}) {
return Column(
children: [
Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: iconBgColor,
borderRadius: BorderRadius.circular(14),
),
child: SvgPicture.asset(
iconPath,
height: 28,
width: 28,
),
),
const SizedBox(height: 6),
Text(
title,
style: const TextStyle(
fontSize: 13,
fontWeight: FontWeight.w600,
fontFamily: "Plus Jakarta Sans",
color: Colors.black87,
),
),
const SizedBox(height: 2),
Text(
value,
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 13,
fontWeight: FontWeight.w400,
fontFamily: "Plus Jakarta Sans",
color: Colors.black54,
),
),
],
);
}
Widget _twoColumnItem(String title, String value) {
return Column(
children: [
Text(
title,
style: const TextStyle(
fontSize: 13,
fontWeight: FontWeight.w600,
fontFamily: "Plus Jakarta Sans",
color: Colors.black87,
),
),
const SizedBox(height: 4),
Text(
value,
style: const TextStyle(
fontSize: 13,
fontWeight: FontWeight.w400,
fontFamily: "Plus Jakarta Sans",
color: Colors.black54,
),
),
],
);
}
} }
...@@ -3,10 +3,10 @@ import 'dart:io'; ...@@ -3,10 +3,10 @@ import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:gen_rentals/Screens/BillDetailListScreen.dart'; import 'package:gen_rentals/Screens/BillScreens/BillDetailListScreen.dart';
import 'package:gen_rentals/Screens/DashboardScreen.dart'; import 'package:gen_rentals/Screens/DashboardScreen.dart';
import 'package:gen_rentals/Screens/ProductsDetailScreen.dart'; import 'package:gen_rentals/Screens/ProductsDetailScreen.dart';
import 'package:gen_rentals/Screens/TransactionsScreen.dart'; import 'package:gen_rentals/Screens/TransactionScreens/TransactionsScreen.dart';
import '../Utility/CustomSnackbar.dart'; import '../Utility/CustomSnackbar.dart';
import '../Utility/SharedpreferencesService.dart'; import '../Utility/SharedpreferencesService.dart';
import 'authScreen/LoginScreen.dart'; import 'authScreen/LoginScreen.dart';
......
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:gen_rentals/Utility/AppColors.dart';
enum BillStatusType { paid, pending }
class BillStatusToast extends StatelessWidget {
final BillStatusType type;
final String amount;
final String orderId;
final String date;
final String product;
final String productPrice;
final String total;
final VoidCallback onDownload;
final VoidCallback? onPayNow;
const BillStatusToast({
super.key,
required this.type,
required this.amount,
required this.orderId,
required this.date,
required this.product,
required this.productPrice,
required this.total,
required this.onDownload,
this.onPayNow,
});
@override
Widget build(BuildContext context) {
final isPaid = type == BillStatusType.paid;
return Dialog(
insetPadding: const EdgeInsets.all(16),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// ✅ or ❌ icon
Container(
decoration: BoxDecoration(
color: isPaid
? Colors.green.withOpacity(0.1)
: Colors.red.withOpacity(0.1),
shape: BoxShape.circle,
),
padding: const EdgeInsets.all(12),
child: Icon(
isPaid ? Icons.check_circle : Icons.access_time_filled,
color: isPaid ? Colors.green : Colors.red,
size: 40,
),
),
const SizedBox(height: 12),
Text(
isPaid ? "Payment Receipt" : "Bill Pending",
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
fontFamily: "Plus Jakarta Sans",
),
),
const SizedBox(height: 6),
Text(
"₹$amount",
style: const TextStyle(
fontSize: 28,
fontWeight: FontWeight.w700,
color: Colors.black,
),
),
const SizedBox(height: 18),
// Details
const Divider(thickness: 1),
Align(
alignment: Alignment.centerLeft,
child: Text(
"Payment Details",
style: TextStyle(
fontSize: 15,
color: Colors.grey[700],
fontWeight: FontWeight.w500,
),
),
),
const SizedBox(height: 10),
_detailRow("Order ID", "#$orderId", true),
_detailRow("Date", date, false),
if (isPaid) _detailRow("Payment Mode", "UPI", false),
const SizedBox(height: 10),
const Divider(thickness: 1),
Align(
alignment: Alignment.centerLeft,
child: Text(
"Products",
style: TextStyle(
fontSize: 15,
color: Colors.grey[700],
fontWeight: FontWeight.w500,
),
),
),
const SizedBox(height: 10),
_detailRow(product, "₹${productPrice}", false),
const SizedBox(height: 10),
const Divider(thickness: 1),
_detailRow("Total", "₹$total", true),
const SizedBox(height: 20),
// Bottom buttons
if (isPaid)
SizedBox(
width: double.infinity,
child: ElevatedButton.icon(
onPressed: onDownload,
icon: const Icon(Icons.download, color: Colors.white),
label: const Text(
"Download Receipt",
style: TextStyle(
fontSize: 16,
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w600,
color: Colors.white,
),
),
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.amountText,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50),
),
padding: const EdgeInsets.symmetric(vertical: 14),
),
),
)
else
Row(
children: [
Expanded(
child: OutlinedButton.icon(
onPressed: onDownload,
icon: const Icon(Icons.download, color: Colors.black),
label: const Text(
"Download",
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.w600,
),
),
style: OutlinedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50),
),
side: const BorderSide(color: Colors.black26),
padding: const EdgeInsets.symmetric(vertical: 14),
),
),
),
const SizedBox(width: 10),
Expanded(
child: ElevatedButton(
onPressed: onPayNow,
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.amountText,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50),
),
padding: const EdgeInsets.symmetric(vertical: 14),
),
child: const Text(
"Pay Now",
style: TextStyle(
fontSize: 16,
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w600,
color: Colors.white,
),
),
),
),
],
),
],
),
),
);
}
Widget _detailRow(String title, String value, bool highlight) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(title,
style: const TextStyle(
fontSize: 14,
color: Colors.black54,
)),
Text(
value,
style: TextStyle(
fontSize: 14,
fontWeight: highlight ? FontWeight.w600 : FontWeight.w500,
color: highlight ? Colors.black : Colors.black87,
),
),
],
),
);
}
}
...@@ -2,9 +2,11 @@ import 'package:flutter/material.dart'; ...@@ -2,9 +2,11 @@ import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../Notifier/TransactionsProvider.dart'; import '../../Notifier/TransactionsProvider.dart';
import '../Utility/AppColors.dart'; import '../../Utility/AppColors.dart';
import 'DashboardScreen.dart'; import '../../Utility/CustomSnackbar.dart';
import '../DashboardScreen.dart';
import 'BillStatusToast.dart';
class TransactionsScreen extends StatefulWidget { class TransactionsScreen extends StatefulWidget {
final String sessionId; final String sessionId;
...@@ -95,12 +97,74 @@ class _TransactionsScreenState extends State<TransactionsScreen> { ...@@ -95,12 +97,74 @@ class _TransactionsScreenState extends State<TransactionsScreen> {
color: Colors.black, color: Colors.black,
), ),
), ),
const SizedBox(height: 8), SizedBox(height: 8),
...items.map((txn) => _buildTransactionItem( ...items.map((txn) => InkResponse(
type: txn.type ?? "", onTap: () async {
title: txn.purpose ?? "-",
date: txn.date ?? "", // Step 1: Fetch receipt details
amount: "₹${txn.amount ?? "0.00"}", await provider.fetchPaymentReceiptDetails(
widget.sessionId,
widget.accId,
txn.paymentId.toString(),
);
// Step 2: If found, show the toast dialog
if (provider.receiptDetails != null) {
final receipt = provider.receiptDetails!;
showDialog(
context: context,
builder: (_) => BillStatusToast(
type: BillStatusType.paid,
amount: receipt.cAmount ?? "0",
orderId: receipt.billId ?? "",
date: receipt.prDate ?? "",
product: receipt.narration ?? "N/A",
productPrice: receipt.dAmount ?? "",
total: receipt.cAmount ?? "0",
/// Use provider for Download
onDownload: () async {
Navigator.pop(context); // Close toast before downloading
await provider.downloadPaymentReceipt(
context,
widget.sessionId,
receipt.id ?? "",
widget.accId,
);
},
/// Handle Pay Now
onPayNow: () {
Navigator.pop(context);
// Navigate to payment screen or trigger payment flow
// Navigator.push(
// context,
// MaterialPageRoute(
// builder: (_) => PaymentScreen(
// accId: widget.accId,
// sessionId: widget.sessionId,
// ledgerId: receipt.id ?? "",
// ),
// ),
// );
},
),
);
} else {
CustomSnackBar.showError(
context: context,
message: "Unable to fetch receipt details",
);
}
},
child: _buildTransactionItem(
type: txn.type ?? "",
title: txn.purpose ?? "-",
date: txn.date ?? "",
amount: "₹${txn.amount ?? "0.00"}",
),
)), )),
], ],
); );
...@@ -418,7 +482,8 @@ class _TransactionsScreenState extends State<TransactionsScreen> { ...@@ -418,7 +482,8 @@ class _TransactionsScreenState extends State<TransactionsScreen> {
return PaymentBottomSheetContent( return PaymentBottomSheetContent(
payTotal: payTotal, payTotal: payTotal,
payBill: payBill, payBill: payBill,
flag: false, billFlag: false,
partFlag: true,
); );
}, },
); );
......
...@@ -207,11 +207,11 @@ class _OtpScreenState extends State<OtpScreen> { ...@@ -207,11 +207,11 @@ class _OtpScreenState extends State<OtpScreen> {
filled: true, filled: true,
fillColor: Colors.white, fillColor: Colors.white,
enabledBorder: OutlineInputBorder( enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(50), borderRadius: BorderRadius.circular(60),
borderSide: BorderSide(color: Colors.grey.withOpacity(0.5), width: 1), borderSide: BorderSide(color: Colors.grey.withOpacity(0.5), width: 1),
), ),
focusedBorder: OutlineInputBorder( focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(50), borderRadius: BorderRadius.circular(60),
borderSide: const BorderSide(color: Color(0xFF008CDE), width: 2), borderSide: const BorderSide(color: Color(0xFF008CDE), width: 2),
), ),
), ),
......
...@@ -11,7 +11,9 @@ const transactionsUrl = "${baseUrl}transactions"; ...@@ -11,7 +11,9 @@ const transactionsUrl = "${baseUrl}transactions";
const balanceUrl = "${baseUrl}balance"; const balanceUrl = "${baseUrl}balance";
const billDetailsUrl = "${baseUrl}bill_details"; const billDetailsUrl = "${baseUrl}bill_details";
const billListUrl = "${baseUrl}bill_list"; const billListUrl = "${baseUrl}bill_list";
const billProductUrl = "${baseUrl}bill_product"; const downloadBillUrl = "${baseUrl}download_bill";
const paymentReceiptDetailsUrl = "${baseUrl}payment_receipt_details";
const downloadReceiptUrl = "${baseUrl}download_receipt";
/// info /// info
const checkInOutSubmitUrl = "${baseUrl}check_in_out_submit"; const checkInOutSubmitUrl = "${baseUrl}check_in_out_submit";
...@@ -36,4 +38,5 @@ const ticketListUrl = "${baseUrl}ticket_list"; ...@@ -36,4 +38,5 @@ const ticketListUrl = "${baseUrl}ticket_list";
/// dashboard and login /// dashboard and login
const fetchMobileUrl = "${baseUrl2}Rental_Auth/fetch_mobile_number"; const fetchMobileUrl = "${baseUrl2}Rental_Auth/fetch_mobile_number";
const fetchOtpUrl = "${baseUrl2}Rental_Auth/login"; const fetchOtpUrl = "${baseUrl2}Rental_Auth/login";
const dashboardUrl = "${baseUrl2}Rental_Home/dashboard"; const dashboardUrl = "${baseUrl2}Rental_Home/dashboard";
\ No newline at end of file const logoutUrl = "${baseUrl2}Rental_Auth/logout";
\ No newline at end of file
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import 'package:gen_rentals/Models/CommonResponse.dart'; import 'package:gen_rentals/Models/CommonResponse.dart';
import 'package:gen_rentals/Models/HelpAndEnquiryModels/TicketChatDisplayResponse.dart'; import 'package:gen_rentals/Models/HelpAndEnquiryModels/TicketChatDisplayResponse.dart';
import 'package:gen_rentals/Models/billListResponse.dart'; import 'package:gen_rentals/Models/BillsModels/billListResponse.dart';
import 'package:gen_rentals/Models/TransactionModels/PaymentReceiptDetailsResponse.dart';
import 'package:gen_rentals/Models/billProductResponse.dart'; import 'package:gen_rentals/Models/billProductResponse.dart';
import 'package:gen_rentals/Models/orderDetailsBillResponse.dart';
import 'package:gen_rentals/Models/orderDetailsMainResponse.dart'; import 'package:gen_rentals/Models/orderDetailsMainResponse.dart';
import 'package:gen_rentals/Models/orderDetailsProductResponse.dart'; import 'package:gen_rentals/Models/orderDetailsProductResponse.dart';
import 'package:gen_rentals/Models/SubscribeOrderDetailsResponse.dart'; import 'package:gen_rentals/Models/SubscribeOrderDetailsResponse.dart';
import 'package:gen_rentals/Models/HelpAndEnquiryModels/ticketListResponse.dart'; import 'package:gen_rentals/Models/HelpAndEnquiryModels/ticketListResponse.dart';
import 'package:gen_rentals/Notifier/billDetailsResponse.dart'; import '../Models/BillsModels/BillDetailsResponse.dart';
import '../Models/DashboardResponse.dart'; import '../Models/DashboardResponse.dart';
import '../Models/RentalPaymentDetailsResponse.dart'; import '../Models/RentalPaymentDetailsResponse.dart';
import '../Models/TransactionsResponse.dart'; import '../Models/TransactionModels/TransactionsResponse.dart';
import '../Models/orderDetailsBillResponse.dart';
import '../Models/rentalAccountResponse.dart'; import '../Models/rentalAccountResponse.dart';
import '../Models/rentalContactResponse.dart'; import '../Models/rentalContactResponse.dart';
import '../Notifier/RentalContactProvider .dart'; import '../Notifier/RentalContactProvider .dart';
import 'api_URLs.dart'; import 'api_URLs.dart';
import 'api_post_request.dart'; import 'api_post_request.dart';
import 'package:http/http.dart' as http show MultipartFile;
class ApiCalling { class ApiCalling {
...@@ -74,6 +74,31 @@ class ApiCalling { ...@@ -74,6 +74,31 @@ class ApiCalling {
} }
} }
static Future<CommonResponse?> logoutApi(
String accId,
String sessionId,
Map<String, String> deviceDetails
) async {
debugPrint("############################### Api calling ");
try {
Map<String, String> data = {
"acc_id": accId,
"session_id": sessionId,
};
final res = await post(data, fetchOtpUrl, {});
if (res != null) {
return CommonResponse.fromJson(jsonDecode(res.body));
} else {
debugPrint("Null Response");
return null;
}
} catch (e) {
debugPrint("❌ API Error: $e");
return null;
}
}
/// Fetch Rental Account Info /// Fetch Rental Account Info
static Future<RentalAccountResponse?> fetchRentalAccountInfoApi( static Future<RentalAccountResponse?> fetchRentalAccountInfoApi(
...@@ -103,13 +128,13 @@ class ApiCalling { ...@@ -103,13 +128,13 @@ class ApiCalling {
/// Fetch Bill List /// Fetch Bill List
static Future<BillListResponse?> fetchBillListApi( static Future<BillListResponse?> fetchBillListApi(
String sessionId, String sessionId,
String empId, String orderId,
String accId, String accId,
) async { ) async {
try { try {
Map<String, String> data = { Map<String, String> data = {
"session_id": sessionId, "session_id": sessionId,
"emp_id": empId, "order_id": orderId,
"acc_id": accId, "acc_id": accId,
}; };
...@@ -130,13 +155,13 @@ class ApiCalling { ...@@ -130,13 +155,13 @@ class ApiCalling {
/// Fetch Bill Details /// Fetch Bill Details
static Future<BillDetailsResponse?> fetchBillDetailsApi( static Future<BillDetailsResponse?> fetchBillDetailsApi(
String sessionId, String sessionId,
String empId, String accId,
String billId, String billId,
) async { ) async {
try { try {
Map<String, String> data = { Map<String, String> data = {
"session_id": sessionId, "session_id": sessionId,
"emp_id": empId, "acc_id": accId,
"bill_id": billId, "bill_id": billId,
}; };
...@@ -155,8 +180,8 @@ class ApiCalling { ...@@ -155,8 +180,8 @@ class ApiCalling {
} }
/// Fetch Bill Product /// Bill Download api
static Future<BillProductResponse?> fetchBillProductApi( static Future<CommonResponse?> billDownloadApi(
String sessionId, String sessionId,
String empId, String empId,
String accId, String accId,
...@@ -164,51 +189,82 @@ class ApiCalling { ...@@ -164,51 +189,82 @@ class ApiCalling {
try { try {
Map<String, String> data = { Map<String, String> data = {
"session_id": sessionId, "session_id": sessionId,
"emp_id": empId, "bill_id": empId,
"acc_id": accId,
};
final res = await post(data, downloadBillUrl, {});
if (res != null) {
return CommonResponse.fromJson(jsonDecode(res.body));
} else {
debugPrint("Null Response");
return null;
}
} catch (e) {
debugPrint("❌ API Error (billDownload): $e");
return null;
}
}
/// Payment Receipt Details api
static Future<PaymentReceiptDetailsResponse?> fetchPaymentReceiptDetailsApi(
String sessionId,
String accId,
String ledgerId,
) async {
try {
Map<String, String> data = {
"session_id": sessionId,
"acc_id": accId, "acc_id": accId,
"ledger_id": ledgerId,
}; };
final res = await post(data, billProductUrl, {}); final res = await post(data, paymentReceiptDetailsUrl, {});
debugPrint("TR Response ${res?.body}");
if (res != null) { if (res != null) {
return BillProductResponse.fromJson(jsonDecode(res.body)); return PaymentReceiptDetailsResponse.fromJson(jsonDecode(res.body));
} else { } else {
debugPrint("Null Response"); debugPrint("Null Response");
return null; return null;
} }
} catch (e) { } catch (e) {
debugPrint("❌ API Error (fetchBillProduct): $e"); debugPrint("❌ API Error (paymentReceiptDetails): $e");
return null; return null;
} }
} }
/// Fetch Order Detail Bill /// Payment Receipt DownloadApi
static Future<OrderDetailsBillResponse?> fetchOrderDetailBillApi( static Future<CommonResponse?> paymentReceiptDownloadApi(
String sessionId, String sessionId,
String empId, String ledgerId,
String orderId, String accId,
) async { ) async {
try { try {
Map<String, String> data = { Map<String, String> data = {
"session_id": sessionId, "session_id": sessionId,
"emp_id": empId, "ledger_id": ledgerId,
"ord_id": orderId, "acc_id": accId,
}; };
final res = await post(data, orderDetailsBillUrl, {}); final res = await post(data, downloadReceiptUrl, {});
debugPrint("DownloadApi Response${res}");
if (res != null) { if (res != null) {
return OrderDetailsBillResponse.fromJson(jsonDecode(res.body)); return CommonResponse.fromJson(jsonDecode(res.body));
} else { } else {
debugPrint("Null Response"); debugPrint("Null Response");
return null; return null;
} }
} catch (e) { } catch (e) {
debugPrint("❌ API Error (fetchOrderDetailBill): $e"); debugPrint("❌ API Error (billDownload): $e");
return null; return null;
} }
} }
/// Fetch Subscribe Order Details /// Fetch Subscribe Order Details
static Future<SubscribeOrderDetailsResponse?> fetchSubsOrderDetailApi( static Future<SubscribeOrderDetailsResponse?> fetchSubsOrderDetailApi(
String sessionId, String sessionId,
...@@ -371,15 +427,42 @@ class ApiCalling { ...@@ -371,15 +427,42 @@ class ApiCalling {
return null; return null;
} }
} }
//
// static Future<addTicketResponse?> addTicketAPI(
// type, description, List<http.MultipartFile> newList) async {
// if (await CheckHeaderValidity()) {
// try {
// Map<String, String> data = {
// 'type': type.toString(),
// 'description': description.toString(),
// //ticket_image
// };
// // print("Add ticket ${data}");
// final header = await HeaderValues();
// final res = await PostMultipleImagesNew(
// data, addTicketsUrl, header, newList);
// if (res != null) {
// return addTicketResponse.fromJson(jsonDecode(res));
// } else {
// return null;
// }
// } catch (e) {
// debugPrint('hello bev=bug $e ');
// return null;
// }
// } else {
// return addTicketResponse(error: "401");
// }
// }
/// Send Message Chat Api
/// Send Message Chat Api /// Send Message Chat Api
static Future<CommonResponse?> addMessageApi( static Future<CommonResponse?> addMessageApi(
String sessionId, String sessionId,
String accId, String accId,
String ticketId, String ticketId,
String msgText, String msgText,
List<File>? images, List<http.MultipartFile>? images,
) async { ) async {
try { try {
Map<String, String> body = { Map<String, String> body = {
...@@ -394,13 +477,15 @@ class ApiCalling { ...@@ -394,13 +477,15 @@ class ApiCalling {
// Add any other headers you need (auth tokens, etc.) // Add any other headers you need (auth tokens, etc.)
}; };
final res = await postMessageWithImages( debugPrint("Data to addTicketApi${body}");
final res = await PostMultipleImagesNew(
body, body,
addMessageUrl, // Your API endpoint addMessageUrl, // Your API endpoint
headers, headers,
images ?? [], // Pass empty list if no images images ?? [], // Pass empty list if no images
); );
debugPrint("Response from addMessageApi ${res}");
if (res != null) { if (res != null) {
return CommonResponse.fromJson(jsonDecode(res)); return CommonResponse.fromJson(jsonDecode(res));
} else { } else {
...@@ -420,6 +505,7 @@ class ApiCalling { ...@@ -420,6 +505,7 @@ class ApiCalling {
String type, String type,
String description, String description,
String orderId, String orderId,
String otherReason,
List<File>? images, List<File>? images,
) async { ) async {
...@@ -430,7 +516,7 @@ class ApiCalling { ...@@ -430,7 +516,7 @@ class ApiCalling {
"type": type, "type": type,
"des": description, "des": description,
"order_id": orderId, "order_id": orderId,
"other_reason": otherReason,
}; };
Map<String, String> headers = { Map<String, String> headers = {
......
...@@ -181,20 +181,50 @@ Future<String?> postImageNew( ...@@ -181,20 +181,50 @@ Future<String?> postImageNew(
//hotel_image //hotel_image
//other_image //other_image
Future<String?> PostMultipleImagesNew( // 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, Map<String, String> body,
String urlLink, String urlLink,
Map<String, String> headers, Map<String, String> headers,
List<http.MultipartFile> newList, List<http.MultipartFile> newList,
List<http.MultipartFile> newList1, List<http.MultipartFile> newList1,
List<http.MultipartFile> newList2,
) async { ) async {
try { try {
var req = http.MultipartRequest('POST', Uri.parse(urlLink)); var req = http.MultipartRequest('POST', Uri.parse(urlLink));
req.headers.addAll(headers); req.headers.addAll(headers);
req.files.addAll(newList); req.files.addAll(newList);
req.files.addAll(newList1); req.files.addAll(newList1);
req.files.addAll(newList2);
req.fields.addAll(body); req.fields.addAll(body);
var res = await req.send(); var res = await req.send();
...@@ -213,18 +243,16 @@ Future<String?> PostMultipleImagesNew( ...@@ -213,18 +243,16 @@ Future<String?> PostMultipleImagesNew(
} }
} }
Future<String?> PostMultipleImagesNew2( Future<String?> PostMultipleImages(
Map<String, String> body, Map<String, String> body,
String urlLink, String urlLink,
Map<String, String> headers, Map<String, String> headers,
List<http.MultipartFile> newList, List<http.MultipartFile> newList,
List<http.MultipartFile> newList1,
) async { ) async {
try { try {
var req = http.MultipartRequest('POST', Uri.parse(urlLink)); var req = http.MultipartRequest('POST', Uri.parse(urlLink));
req.headers.addAll(headers); req.headers.addAll(headers);
req.files.addAll(newList); req.files.addAll(newList);
req.files.addAll(newList1);
req.fields.addAll(body); req.fields.addAll(body);
var res = await req.send(); var res = await req.send();
...@@ -242,13 +270,11 @@ Future<String?> PostMultipleImagesNew2( ...@@ -242,13 +270,11 @@ Future<String?> PostMultipleImagesNew2(
return null; return null;
} }
} }
Future<String?> PostMultipleImagesNew(
Future<String?> PostMultipleImages( Map<String, String> body,
Map<String, String> body, String urlLink,
String urlLink, Map<String, String> headers,
Map<String, String> headers, List<http.MultipartFile> newList) async {
List<http.MultipartFile> newList,
) async {
try { try {
var req = http.MultipartRequest('POST', Uri.parse(urlLink)); var req = http.MultipartRequest('POST', Uri.parse(urlLink));
req.headers.addAll(headers); req.headers.addAll(headers);
...@@ -271,6 +297,7 @@ Future<String?> PostMultipleImages( ...@@ -271,6 +297,7 @@ Future<String?> PostMultipleImages(
} }
} }
/// Send message with multiple images /// Send message with multiple images
Future<String?> postMessageWithImages( Future<String?> postMessageWithImages(
Map<String, String> body, Map<String, String> body,
......
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:photo_view/photo_view.dart';
class FullScreenImageViewer extends StatefulWidget {
final String imageUrl;
const FullScreenImageViewer({super.key, required this.imageUrl});
@override
State<FullScreenImageViewer> createState() => _FullScreenImageViewerState();
}
class _FullScreenImageViewerState extends State<FullScreenImageViewer> {
PhotoViewController controller = PhotoViewController();
PhotoViewScaleStateController scaleStateController = PhotoViewScaleStateController();
bool _showControls = true;
@override
void initState() {
super.initState();
// Auto-hide controls after 3 seconds
Future.delayed(const Duration(seconds: 3), () {
if (mounted) {
setState(() {
_showControls = false;
});
}
});
}
void _toggleControls() {
setState(() {
_showControls = !_showControls;
});
}
void _resetImage() {
controller.reset();
scaleStateController.reset();
}
void _rotateImage() {
final double currentRotation = controller.rotation;
controller.rotation = currentRotation + 90.0;
}
@override
Widget build(BuildContext context) {
return SafeArea(
top: false,
child: Scaffold(
backgroundColor: Color(0xFFFFFFFF),
body: Stack(
children: [
// Main Photo View
GestureDetector(
onTap: _toggleControls,
child: PhotoView(
imageProvider: NetworkImage(widget.imageUrl),
controller: controller,
scaleStateController: scaleStateController,
backgroundDecoration: const BoxDecoration(
color: Color(0xFF444444),
),
minScale: PhotoViewComputedScale.contained * 0.8,
maxScale: PhotoViewComputedScale.covered * 4,
initialScale: PhotoViewComputedScale.contained,
basePosition: Alignment.center,
filterQuality: FilterQuality.high,
enableRotation: true,
gestureDetectorBehavior: HitTestBehavior.opaque,
loadingBuilder: (context, event) => Center(
child: Container(
width: 40,
height: 40,
child: const CircularProgressIndicator(
color: Colors.white,
strokeWidth: 2,
),
),
),
errorBuilder: (context, error, stackTrace) => Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(
Icons.error_outline,
color: Colors.white,
size: 50,
),
const SizedBox(height: 16),
Text(
'Failed to load image',
style: TextStyle(
color: Colors.white.withOpacity(0.8),
fontSize: 16,
),
),
],
),
),
),
),
// Controls Overlay
if (_showControls) ...[
// Top Bar
Positioned(
top: MediaQuery.of(context).padding.top,
left: 0,
right: 0,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.black.withOpacity(0.7),
Colors.black.withOpacity(0.3),
Colors.transparent,
],
),
),
child: Row(
children: [
// Close Button
GestureDetector(
onTap: () => Navigator.pop(context),
child: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.5),
shape: BoxShape.circle,
),
child: const Icon(
Icons.close,
color: Colors.white,
size: 24,
),
),
),
const Spacer(),
// Reset Button
GestureDetector(
onTap: _resetImage,
child: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.5),
shape: BoxShape.circle,
),
child: const Icon(
Icons.refresh,
color: Colors.white,
size: 24,
),
),
),
const SizedBox(width: 12),
// Rotate Button
GestureDetector(
onTap: _rotateImage,
child: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.5),
shape: BoxShape.circle,
),
child: const Icon(
Icons.rotate_90_degrees_ccw,
color: Colors.white,
size: 24,
),
),
),
],
),
),
),
// Bottom Info Bar
Positioned(
bottom: 0,
left: 0,
right: 0,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.bottomCenter,
end: Alignment.topCenter,
colors: [
Colors.black.withOpacity(0.7),
Colors.black.withOpacity(0.3),
Colors.transparent,
],
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Zoom Instructions
Row(
children: [
_buildInstructionIcon(Icons.touch_app, "Double tap to zoom"),
const SizedBox(width: 16),
_buildInstructionIcon(Icons.pan_tool, "Pan to move"),
const SizedBox(width: 16),
_buildInstructionIcon(Icons.rotate_right, "Pinch to rotate"),
],
),
const SizedBox(height: 12),
// Image Info
Text(
"Image Preview",
style: TextStyle(
color: Colors.white.withOpacity(0.9),
fontSize: 14,
fontWeight: FontWeight.w500,
),
),
],
),
),
),
],
// Double Tap Zoom Hint (appears briefly)
Positioned(
bottom: MediaQuery.of(context).padding.bottom + 100,
left: 0,
right: 0,
child: AnimatedOpacity(
opacity: _showControls ? 1.0 : 0.0,
duration: const Duration(milliseconds: 300),
child: const Center(
child: Text(
"Double tap to zoom",
style: TextStyle(
color: Colors.white,
fontSize: 14,
fontWeight: FontWeight.w400,
),
),
),
),
),
],
),
// Bottom Navigation Bar with additional controls
bottomNavigationBar: _showControls
? Container(
height: 60,
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.7),
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(12),
topRight: Radius.circular(12),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
// Zoom In
IconButton(
onPressed: () {
final double currentScale = controller.scale ?? 1.0;
controller.scale = currentScale * 1.5;
},
icon: const Icon(Icons.zoom_in, color: Colors.white),
tooltip: "Zoom In",
),
// Zoom Out
IconButton(
onPressed: () {
final double currentScale = controller.scale ?? 1.0;
controller.scale = currentScale / 1.5;
},
icon: const Icon(Icons.zoom_out, color: Colors.white),
tooltip: "Zoom Out",
),
// Reset
IconButton(
onPressed: _resetImage,
icon: const Icon(Icons.fit_screen, color: Colors.white),
tooltip: "Reset",
),
// Rotate
IconButton(
onPressed: _rotateImage,
icon: const Icon(Icons.rotate_90_degrees_ccw, color: Colors.white),
tooltip: "Rotate 90°",
),
],
),
)
: null,
),
);
}
Widget _buildInstructionIcon(IconData icon, String text) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(icon, color: Colors.white.withOpacity(0.7), size: 16),
const SizedBox(width: 4),
Text(
text,
style: TextStyle(
color: Colors.white.withOpacity(0.7),
fontSize: 12,
),
),
],
);
}
@override
void dispose() {
controller.dispose();
scaleStateController.dispose();
super.dispose();
}
}
\ No newline at end of file
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:gen_rentals/Notifier/BillProvider.dart';
import 'package:gen_rentals/Notifier/DashboardProvider.dart'; import 'package:gen_rentals/Notifier/DashboardProvider.dart';
import 'package:gen_rentals/Notifier/HelpAndEnquiryProvider.dart'; import 'package:gen_rentals/Notifier/HelpAndEnquiryProvider.dart';
import 'package:gen_rentals/Notifier/TransactionsProvider.dart'; import 'package:gen_rentals/Notifier/TransactionsProvider.dart';
...@@ -34,6 +35,7 @@ class MyApp extends StatelessWidget { ...@@ -34,6 +35,7 @@ class MyApp extends StatelessWidget {
ChangeNotifierProvider(create: (_) => SubscribeOrderDetailsProvider()), ChangeNotifierProvider(create: (_) => SubscribeOrderDetailsProvider()),
ChangeNotifierProvider(create: (_) => TransactionsProvider()), ChangeNotifierProvider(create: (_) => TransactionsProvider()),
ChangeNotifierProvider(create: (_) => HelpAndEnquiryProvider()), ChangeNotifierProvider(create: (_) => HelpAndEnquiryProvider()),
ChangeNotifierProvider(create: (_) => BillProvider()),
], ],
child: Consumer<ThemeProvider>( child: Consumer<ThemeProvider>(
builder: (context, themeProvider, child) { builder: (context, themeProvider, child) {
...@@ -41,24 +43,7 @@ class MyApp extends StatelessWidget { ...@@ -41,24 +43,7 @@ class MyApp extends StatelessWidget {
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false,
title: 'Gen Rentals', title: 'Gen Rentals',
theme: ThemeData( theme: ThemeData(
brightness: themeProvider.isDark ? Brightness.dark : Brightness.light, fontFamily: 'PoppinsRegular',
fontFamily: 'Poppins',
textTheme: const TextTheme(
bodyMedium: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w400,
height: 1.5,
letterSpacing: -0.12,
),
titleMedium: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
),
labelLarge: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w700,
),
),
), ),
home: const SplashScreen(), home: const SplashScreen(),
); );
......
...@@ -157,10 +157,10 @@ packages: ...@@ -157,10 +157,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: fake_async name: fake_async
sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc" sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.3.2" version: "1.3.3"
ffi: ffi:
dependency: transitive dependency: transitive
description: description:
...@@ -412,26 +412,26 @@ packages: ...@@ -412,26 +412,26 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: leak_tracker name: leak_tracker
sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "10.0.8" version: "11.0.2"
leak_tracker_flutter_testing: leak_tracker_flutter_testing:
dependency: transitive dependency: transitive
description: description:
name: leak_tracker_flutter_testing name: leak_tracker_flutter_testing
sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.9" version: "3.0.10"
leak_tracker_testing: leak_tracker_testing:
dependency: transitive dependency: transitive
description: description:
name: leak_tracker_testing name: leak_tracker_testing
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.1" version: "3.0.2"
lints: lints:
dependency: transitive dependency: transitive
description: description:
...@@ -504,6 +504,14 @@ packages: ...@@ -504,6 +504,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.0" version: "2.1.0"
open_filex:
dependency: "direct dev"
description:
name: open_filex
sha256: "9976da61b6a72302cf3b1efbce259200cd40232643a467aac7370addf94d6900"
url: "https://pub.dev"
source: hosted
version: "4.7.0"
package_info_plus: package_info_plus:
dependency: "direct dev" dependency: "direct dev"
description: description:
...@@ -537,7 +545,7 @@ packages: ...@@ -537,7 +545,7 @@ packages:
source: hosted source: hosted
version: "1.1.0" version: "1.1.0"
path_provider: path_provider:
dependency: transitive dependency: "direct dev"
description: description:
name: path_provider name: path_provider
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
...@@ -640,6 +648,14 @@ packages: ...@@ -640,6 +648,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.1.0" version: "6.1.0"
photo_view:
dependency: "direct dev"
description:
name: photo_view
sha256: "8036802a00bae2a78fc197af8a158e3e2f7b500561ed23b4c458107685e645bb"
url: "https://pub.dev"
source: hosted
version: "0.14.0"
platform: platform:
dependency: transitive dependency: transitive
description: description:
...@@ -841,10 +857,10 @@ packages: ...@@ -841,10 +857,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: test_api name: test_api
sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.4" version: "0.7.6"
typed_data: typed_data:
dependency: transitive dependency: transitive
description: description:
...@@ -889,10 +905,10 @@ packages: ...@@ -889,10 +905,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: vector_math name: vector_math
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.4" version: "2.2.0"
vm_service: vm_service:
dependency: transitive dependency: transitive
description: description:
...@@ -942,5 +958,5 @@ packages: ...@@ -942,5 +958,5 @@ packages:
source: hosted source: hosted
version: "6.5.0" version: "6.5.0"
sdks: sdks:
dart: ">=3.7.2 <4.0.0" dart: ">=3.8.0-0 <4.0.0"
flutter: ">=3.29.0" flutter: ">=3.29.0"
...@@ -60,6 +60,9 @@ dev_dependencies: ...@@ -60,6 +60,9 @@ dev_dependencies:
firebase_core: ^4.1.1 firebase_core: ^4.1.1
device_info_plus: ^10.0.0 device_info_plus: ^10.0.0
image_picker: ^1.0.4 image_picker: ^1.0.4
path_provider: ^2.1.4
open_filex: ^4.4.0
photo_view: ^0.14.0
#flutter_local_notifications: ^17.2.0 #flutter_local_notifications: ^17.2.0
...@@ -94,18 +97,24 @@ flutter: ...@@ -94,18 +97,24 @@ flutter:
# list giving the asset and other descriptors for the font. For # list giving the asset and other descriptors for the font. For
# example: # example:
fonts: fonts:
- family: Poppins
# - family: Trajan Pro
- family: PoppinsRegular
fonts: fonts:
- asset: assets/fonts/Poppins/Poppins-Regular.ttf - asset: assets/fonts/Poppins/Poppins-Regular.ttf
style: normal
- family: PoppinsMedium
fonts:
- asset: assets/fonts/Poppins/Poppins-Medium.ttf - asset: assets/fonts/Poppins/Poppins-Medium.ttf
weight: 500 style: normal
- family: PoppinsBold
fonts:
- asset: assets/fonts/Poppins/Poppins-Bold.ttf - asset: assets/fonts/Poppins/Poppins-Bold.ttf
weight: 700 style: normal
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
# #
# For details regarding fonts from package dependencies, # For details regarding fonts from package dependencies,
# see https://flutter.dev/to/font-from-package # see https://flutter.dev/to/font-from-package
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