Commit 9462b0ba authored by Sai Srinivas's avatar Sai Srinivas
Browse files

Login, otp and new screen added

parent 23aaf199
...@@ -25,7 +25,7 @@ android { ...@@ -25,7 +25,7 @@ android {
applicationId = "in.webgrid.genrentals" applicationId = "in.webgrid.genrentals"
// You can update the following values to match your application needs. // You can update the following values to match your application needs.
// For more information, see: https://flutter.dev/to/review-gradle-config. // For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk = 23 minSdk = flutter.minSdkVersion
targetSdk = 36 targetSdk = 36
versionCode = flutter.versionCode versionCode = flutter.versionCode
versionName = flutter.versionName versionName = flutter.versionName
......
class DashboardResponse { class DashboardResponse {
int? error; String? error;
String? message;
int? exist;
String? city;
String? raname; String? raname;
String? mob; String? balanceAmount;
String? mail; String? message;
String? address;
String? state;
String? accId;
int? otp;
List<Products>? products; List<Products>? products;
DashboardResponse( DashboardResponse(
{this.error, {this.error,
this.message,
this.exist,
this.city,
this.raname, this.raname,
this.mob, this.balanceAmount,
this.mail, this.message,
this.address,
this.state,
this.accId,
this.otp,
this.products}); this.products});
DashboardResponse.fromJson(Map<String, dynamic> json) { DashboardResponse.fromJson(Map<String, dynamic> json) {
error = json['error']; error = json['error'];
message = json['message'];
exist = json['exist'];
city = json['city'];
raname = json['raname']; raname = json['raname'];
mob = json['mob']; balanceAmount = json['balance_amount'];
mail = json['mail']; message = json['message'];
address = json['address'];
state = json['state'];
accId = json['acc_id'];
otp = json['otp'];
if (json['products'] != null) { if (json['products'] != null) {
products = <Products>[]; products = <Products>[];
json['products'].forEach((v) { json['products'].forEach((v) {
...@@ -49,16 +28,9 @@ class DashboardResponse { ...@@ -49,16 +28,9 @@ class DashboardResponse {
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>(); final Map<String, dynamic> data = new Map<String, dynamic>();
data['error'] = this.error; data['error'] = this.error;
data['message'] = this.message;
data['exist'] = this.exist;
data['city'] = this.city;
data['raname'] = this.raname; data['raname'] = this.raname;
data['mob'] = this.mob; data['balance_amount'] = this.balanceAmount;
data['mail'] = this.mail; data['message'] = this.message;
data['address'] = this.address;
data['state'] = this.state;
data['acc_id'] = this.accId;
data['otp'] = this.otp;
if (this.products != null) { if (this.products != null) {
data['products'] = this.products!.map((v) => v.toJson()).toList(); data['products'] = this.products!.map((v) => v.toJson()).toList();
} }
......
...@@ -14,9 +14,7 @@ class DashboardProvider with ChangeNotifier { ...@@ -14,9 +14,7 @@ class DashboardProvider with ChangeNotifier {
/// Fetch Dashboard API /// Fetch Dashboard API
Future<void> fetchDashboard({ Future<void> fetchDashboard({
required String sessionId, required String accId,
required String empId,
required String mob,
}) async { }) async {
_isLoading = true; _isLoading = true;
_errorMessage = null; _errorMessage = null;
...@@ -24,7 +22,7 @@ class DashboardProvider with ChangeNotifier { ...@@ -24,7 +22,7 @@ class DashboardProvider with ChangeNotifier {
try { try {
final response = final response =
await ApiCalling.fetchDashboardApi(sessionId, empId, mob); await ApiCalling.fetchDashboardApi(accId,);
if (response != null) { if (response != null) {
_dashboardData = response; _dashboardData = response;
......
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/HelpScreens/EnquiryScreen.dart';
import 'package:gen_rentals/Screens/HelpScreens/HelpScreen.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/TransactionsScreen.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../Models/DashboardResponse.dart';
import '../Notifier/DashboardProvider.dart'; import '../Notifier/DashboardProvider.dart';
import 'authScreen/LoginScreen.dart';
class DashboardScreen extends StatefulWidget { class DashboardScreen extends StatefulWidget {
const DashboardScreen({super.key}); final String accId;
const DashboardScreen({super.key, required this.accId});
@override @override
State<DashboardScreen> createState() => _DashboardScreenState(); State<DashboardScreen> createState() => _DashboardScreenState();
} }
class _DashboardScreenState extends State<DashboardScreen> { class _DashboardScreenState extends State<DashboardScreen> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
Provider.of<DashboardProvider>(context, listen: false).fetchDashboard( // Only fetch dashboard if accId is valid
sessionId: "4d21382d9e1c4d6e0b7c426d53d89b6b7d48078877f185289092e6fa13bac4b11d417d37738b20b34151b8e638625b3ec013", if (widget.accId.isNotEmpty && widget.accId != "null") {
empId: '5', Provider.of<DashboardProvider>(context, listen: false).fetchDashboard(accId: widget.accId);
mob: '9876543210', } else {
); // If accId is invalid, navigate back to login
WidgetsBinding.instance.addPostFrameCallback((_) {
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => LoginScreen()),
);
});
}
}); });
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final dashboardProvider = Provider.of<DashboardProvider>(context);
final dashboardData = dashboardProvider.dashboardData;
double screenWidth = MediaQuery.of(context).size.width; double screenWidth = MediaQuery.of(context).size.width;
double screenHeight = MediaQuery.of(context).size.height; double screenHeight = MediaQuery.of(context).size.height;
double bottomPadding = MediaQuery.of(context).padding.bottom; double bottomPadding = MediaQuery.of(context).padding.bottom;
// Add null check at the beginning
if (dashboardProvider.isLoading && dashboardData == null) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircularProgressIndicator(),
SizedBox(height: 20),
Text("Loading dashboard..."),
],
),
),
);
}
return SafeArea( return SafeArea(
top: false, top: false,
child: Scaffold( child: Scaffold(
...@@ -131,9 +162,10 @@ class _DashboardScreenState extends State<DashboardScreen> { ...@@ -131,9 +162,10 @@ class _DashboardScreenState extends State<DashboardScreen> {
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
), ),
), ),
const Text( // Use provider data for name
"Mohit Kumar", Text(
style: TextStyle( dashboardData?.raname ?? "Loading...",
style: const TextStyle(
fontFamily: "Poppins", fontFamily: "Poppins",
color: Colors.grey, color: Colors.grey,
fontSize: 16, fontSize: 16,
...@@ -188,9 +220,10 @@ class _DashboardScreenState extends State<DashboardScreen> { ...@@ -188,9 +220,10 @@ class _DashboardScreenState extends State<DashboardScreen> {
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
const Text( // Use provider data for balance amount - FIXED NULL CHECK
"₹24,800", Text(
style: TextStyle( dashboardData?.balanceAmount ?? "0",
style: const TextStyle(
fontFamily: "Poppins", fontFamily: "Poppins",
color: Colors.black, color: Colors.black,
fontSize: 32, fontSize: 32,
...@@ -219,7 +252,6 @@ class _DashboardScreenState extends State<DashboardScreen> { ...@@ -219,7 +252,6 @@ class _DashboardScreenState extends State<DashboardScreen> {
color: Colors.grey.shade500, color: Colors.grey.shade500,
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
// fontStyle: FontStyle.italic,
), ),
), ),
], ],
...@@ -238,64 +270,65 @@ class _DashboardScreenState extends State<DashboardScreen> { ...@@ -238,64 +270,65 @@ class _DashboardScreenState extends State<DashboardScreen> {
// Big card - Any Requirements // Big card - Any Requirements
Expanded( Expanded(
flex: 2, flex: 2,
child: Container( child: InkResponse(
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10), onTap: () {
decoration: BoxDecoration( Navigator.push(
color: Colors.white, context,
borderRadius: BorderRadius.circular(12), MaterialPageRoute(builder: (context) => EnquiryScreen()
border: Border.all( )
color: Colors.grey.shade200, );
width: 1, },
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: Colors.grey.shade200,
width: 1,
),
), ),
// boxShadow: [ child: Column(
// BoxShadow( crossAxisAlignment: CrossAxisAlignment.start,
// color: Colors.black.withOpacity(0.05), mainAxisAlignment: MainAxisAlignment.spaceBetween,
// blurRadius: 8, children: [
// offset: const Offset(0, 2), Column(
// ), crossAxisAlignment: CrossAxisAlignment.start,
// ], children: [
), const SizedBox(height: 16),
child: Column( Text(
crossAxisAlignment: CrossAxisAlignment.start, "Any \nRequirements ?",
mainAxisAlignment: MainAxisAlignment.spaceBetween, style: TextStyle(
children: [ fontFamily: "Poppins",
Column( color: Color(0xFF008CDE),
crossAxisAlignment: CrossAxisAlignment.start, fontSize: 16,
children: [ fontWeight: FontWeight.w400,
),
const SizedBox(height: 16),
Text(
"Any \nRequirements ?",
style: TextStyle(
fontFamily: "Poppins",
color: Color(0xFF008CDE),
fontSize: 16,
fontWeight: FontWeight.w400,
), ),
), const SizedBox(height: 8),
const SizedBox(height: 8), Text(
Text( "Submit your enquiry for requirement",
"Submit your enquiry for requirement", style: TextStyle(
style: TextStyle( fontFamily: "Poppins",
fontFamily: "Poppins", color: Colors.grey.shade600,
color: Colors.grey.shade600, fontSize: 14,
fontSize: 14, ),
), ),
), ],
], ),
), const SizedBox(height: 20),
const SizedBox(height: 20), Row(
Row( mainAxisAlignment: MainAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.end, children: [
children: [ SvgPicture.asset(
SvgPicture.asset( "assets/svg/requirements.svg",
"assets/svg/requirements.svg", height: 58,
height: 58, width: 58,
width: 58, ),
), ],
], ),
), ],
], ),
), ),
), ),
), ),
...@@ -318,13 +351,6 @@ class _DashboardScreenState extends State<DashboardScreen> { ...@@ -318,13 +351,6 @@ class _DashboardScreenState extends State<DashboardScreen> {
color: Colors.grey.shade200, color: Colors.grey.shade200,
width: 1, width: 1,
), ),
// boxShadow: [
// BoxShadow(
// color: Colors.black.withOpacity(0.05),
// blurRadius: 8,
// offset: const Offset(0, 2),
// ),
// ],
), ),
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
...@@ -332,47 +358,52 @@ class _DashboardScreenState extends State<DashboardScreen> { ...@@ -332,47 +358,52 @@ class _DashboardScreenState extends State<DashboardScreen> {
children: [ children: [
Expanded( Expanded(
flex: 5, flex: 5,
child: Column( child: InkResponse(
crossAxisAlignment: CrossAxisAlignment.start, onTap: () {
children: [ Navigator.push(
context,
Row( MaterialPageRoute(builder: (context) => HelpScreen()),
children: [ );
Expanded( },
flex: 5, child: Column(
child: Text( crossAxisAlignment: CrossAxisAlignment.start,
"Have Complaints?", children: [
style: TextStyle( Row(
fontFamily: "Poppins", children: [
color: Color(0xFF008CDE), Expanded(
fontSize: 14, flex: 5,
fontWeight: FontWeight.w400, child: Text(
"Need Help?",
style: TextStyle(
fontFamily: "Poppins",
color: Color(0xFF008CDE),
fontSize: 14,
fontWeight: FontWeight.w400,
),
), ),
), ),
), Expanded(
flex: 2,
Expanded( child: SvgPicture.asset(
flex: 2, "assets/svg/have_compaints.svg",
child: SvgPicture.asset( height: 35,
"assets/svg/have_compaints.svg", width: 35,
height: 35, ),
width: 35,
), ),
],
),
const SizedBox(height: 4),
Text(
"Raise a ticket to resolve your issues.",
style: TextStyle(
fontFamily: "Poppins",
color: Colors.grey.shade600,
fontWeight: FontWeight.w400,
fontSize: 12,
), ),
],
),
const SizedBox(height: 4),
Text(
"Raise a ticket to resolve your issues.",
style: TextStyle(
fontFamily: "Poppins",
color: Colors.grey.shade600,
fontWeight: FontWeight.w400,
fontSize: 12,
), ),
), ],
),
],
), ),
), ),
], ],
...@@ -399,13 +430,6 @@ class _DashboardScreenState extends State<DashboardScreen> { ...@@ -399,13 +430,6 @@ class _DashboardScreenState extends State<DashboardScreen> {
color: Colors.grey.shade200, color: Colors.grey.shade200,
width: 1, width: 1,
), ),
// boxShadow: [
// BoxShadow(
// color: Colors.black.withOpacity(0.05),
// blurRadius: 8,
// offset: const Offset(0, 2),
// ),
// ],
), ),
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
...@@ -421,7 +445,7 @@ class _DashboardScreenState extends State<DashboardScreen> { ...@@ -421,7 +445,7 @@ class _DashboardScreenState extends State<DashboardScreen> {
Expanded( Expanded(
flex: 5, flex: 5,
child: Text( child: Text(
"Know Your Payments!", "Transactions!",
style: TextStyle( style: TextStyle(
fontFamily: "Poppins", fontFamily: "Poppins",
color: Color(0xFF008CDE), color: Color(0xFF008CDE),
...@@ -442,8 +466,7 @@ class _DashboardScreenState extends State<DashboardScreen> { ...@@ -442,8 +466,7 @@ class _DashboardScreenState extends State<DashboardScreen> {
), ),
const SizedBox(height: 4), const SizedBox(height: 4),
Text( Text(
"View your all transactions with us." "View your all transactions with us.",
"",
style: TextStyle( style: TextStyle(
fontFamily: "Poppins", fontFamily: "Poppins",
color: Colors.grey.shade600, color: Colors.grey.shade600,
...@@ -451,11 +474,9 @@ class _DashboardScreenState extends State<DashboardScreen> { ...@@ -451,11 +474,9 @@ class _DashboardScreenState extends State<DashboardScreen> {
fontSize: 12, fontSize: 12,
), ),
), ),
], ],
), ),
), ),
], ],
), ),
), ),
...@@ -487,54 +508,40 @@ class _DashboardScreenState extends State<DashboardScreen> { ...@@ -487,54 +508,40 @@ class _DashboardScreenState extends State<DashboardScreen> {
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
// List of subscribed products // Show loading or products list
Column( if (dashboardProvider.isLoading && dashboardData == null)
children: [ const Center(
// Item 1 - Expiring in 9 Days child: CircularProgressIndicator(),
InkResponse( )
onTap: () { else if (dashboardData?.products == null || dashboardData!.products!.isEmpty)
Navigator.push( const Text(
context, "No products subscribed",
MaterialPageRoute(builder: (context) => ProductsDetailScreen()), style: TextStyle(
); fontFamily: "Poppins",
}, color: Colors.grey,
child: _buildProductItem( fontSize: 14,
productName: "Genesis 125kVA",
plan: "1266",
rentedDate: "Rented on 7th July, 2025",
timeLeft: "2",
expiringText: "Expiring in",
pendingPaymentText: "1 Payment Pending. Please Pay before incurring fines.",
hasPendingPayment: true,
),
), ),
)
const SizedBox(height: 16), else
// List of subscribed products from API
// Item 2 - With Pending Payment Column(
_buildProductItem( children: dashboardData!.products!.map((product) {
productName: "Genesis 85kVA", return Column(
plan: "3999", children: [
rentedDate: "Rented on 7th July, 2025", InkResponse(
timeLeft: "2 ", onTap: () {
expiringText: "months left", Navigator.push(
pendingPaymentText: "1 Payment Pending. Please Pay before incurring fines.", context,
hasPendingPayment: true, MaterialPageRoute(builder: (context) => ProductsDetailScreen()),
), );
},
const SizedBox(height: 16), child: _buildProductItemFromApi(product),
),
// Item 3 - Normal const SizedBox(height: 16),
_buildProductItem( ],
productName: "Genesis 30kVA", );
plan: "3999", }).toList(),
rentedDate: "Rented on 7th July, 2025", ),
timeLeft: "7 ",//image url and color of timeleft
expiringText: "months left",
hasPendingPayment: false,
),
],
),
], ],
), ),
) )
...@@ -553,22 +560,14 @@ class _DashboardScreenState extends State<DashboardScreen> { ...@@ -553,22 +560,14 @@ class _DashboardScreenState extends State<DashboardScreen> {
); );
} }
// Helper widget for product item // Helper widget for product item from API data
Widget _buildProductItem({ Widget _buildProductItemFromApi(Products product) {
required String productName,
required String plan,
required String rentedDate,
required String timeLeft,
String? expiringText = "",
String? pendingPaymentText,
required bool hasPendingPayment,
}) {
return Stack( return Stack(
children: [ children: [
// Background strip for pending payment // Background strip for pending payment
if (hasPendingPayment) if (product.hasPendingPayment == true)
Container( Container(
height: 124 + 50, // Main content height + strip height height: 130 + 50,
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.red.withOpacity(0.6), color: Colors.red.withOpacity(0.6),
...@@ -580,7 +579,7 @@ class _DashboardScreenState extends State<DashboardScreen> { ...@@ -580,7 +579,7 @@ class _DashboardScreenState extends State<DashboardScreen> {
), ),
child: Column( child: Column(
children: [ children: [
SizedBox(height: 138), SizedBox(height: 144),
// Pending payment strip // Pending payment strip
Container( Container(
width: double.infinity, width: double.infinity,
...@@ -602,7 +601,7 @@ class _DashboardScreenState extends State<DashboardScreen> { ...@@ -602,7 +601,7 @@ class _DashboardScreenState extends State<DashboardScreen> {
const SizedBox(width: 6), const SizedBox(width: 6),
Expanded( Expanded(
child: Text( child: Text(
pendingPaymentText ?? "Payment Pending", product.pendingPaymentText ?? "Payment Pending",
style: TextStyle( style: TextStyle(
fontFamily: "Poppins", fontFamily: "Poppins",
color: Colors.black87, color: Colors.black87,
...@@ -643,23 +642,16 @@ class _DashboardScreenState extends State<DashboardScreen> { ...@@ -643,23 +642,16 @@ class _DashboardScreenState extends State<DashboardScreen> {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
// Expiring badge (if any) // Expiring badge
if (expiringText == "months left") if (product.expiringText != null && product.expiringText!.isNotEmpty)
Container( Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6), padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
gradient: const LinearGradient( gradient: _getGradientByColor(product.expiringInColor),
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
Color(0xFFE9FFDD),
Color(0xFFB5FFD1),
],
),
), ),
child: Text( child: Text(
"$timeLeft months left", product.expiringText!,
style: const TextStyle( style: const TextStyle(
color: Colors.black87, color: Colors.black87,
fontSize: 12, fontSize: 12,
...@@ -669,34 +661,11 @@ class _DashboardScreenState extends State<DashboardScreen> { ...@@ -669,34 +661,11 @@ class _DashboardScreenState extends State<DashboardScreen> {
), ),
), ),
if (expiringText == "Expiring in") const SizedBox(height: 8),
Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
gradient: const LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
Color(0xFFFFDDDD),
Color(0xFFFFB5B5),
],
),
),
child: Text(
"Expiring in $timeLeft Days",
style: const TextStyle(
color: Colors.black87,
fontSize: 12,
fontStyle: FontStyle.normal,
fontWeight: FontWeight.w400,
),
),
),
// Product name and plan // Product name and plan
Text( Text(
productName, product.productName ?? "Unknown Product",
style: const TextStyle( style: const TextStyle(
color: Colors.black, color: Colors.black,
fontSize: 16, fontSize: 16,
...@@ -717,21 +686,21 @@ class _DashboardScreenState extends State<DashboardScreen> { ...@@ -717,21 +686,21 @@ class _DashboardScreenState extends State<DashboardScreen> {
), ),
), ),
Text( Text(
"₹$plan", "₹${product.plan ?? "0"}",
style: const TextStyle( style: const TextStyle(
fontFamily: "Poppins", fontFamily: "Poppins",
color: Color(0xFF008CDE), color: Color(0xFF008CDE),
fontSize: 18, fontSize: 18,
fontStyle: FontStyle.normal, fontStyle: FontStyle.normal,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
height: 1.2 height: 1.2
), ),
), ),
const SizedBox(height: 3), const SizedBox(height: 3),
// Rented date // Rented date
Text( Text(
rentedDate, product.rentedDate ?? "Rented date not available",
style: TextStyle( style: TextStyle(
fontFamily: "Poppins", fontFamily: "Poppins",
color: Colors.grey.shade500, color: Colors.grey.shade500,
...@@ -740,9 +709,6 @@ class _DashboardScreenState extends State<DashboardScreen> { ...@@ -740,9 +709,6 @@ class _DashboardScreenState extends State<DashboardScreen> {
fontSize: 12, fontSize: 12,
), ),
), ),
const SizedBox(height: 1),
], ],
), ),
), ),
...@@ -756,7 +722,22 @@ class _DashboardScreenState extends State<DashboardScreen> { ...@@ -756,7 +722,22 @@ class _DashboardScreenState extends State<DashboardScreen> {
color: const Color(0xffF2F2F2), color: const Color(0xffF2F2F2),
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
), ),
child: Image.asset( child: product.productImage != null && product.productImage!.isNotEmpty
? Image.network(
product.productImage!,
height: 80,
width: 80,
fit: BoxFit.contain,
errorBuilder: (context, error, stackTrace) {
return Image.asset(
'assets/images/gene_png.png',
height: 80,
width: 80,
fit: BoxFit.contain,
);
},
)
: Image.asset(
'assets/images/gene_png.png', 'assets/images/gene_png.png',
height: 80, height: 80,
width: 80, width: 80,
...@@ -773,6 +754,30 @@ class _DashboardScreenState extends State<DashboardScreen> { ...@@ -773,6 +754,30 @@ class _DashboardScreenState extends State<DashboardScreen> {
); );
} }
// Helper method to get gradient based on color from API
LinearGradient _getGradientByColor(String? color) {
switch (color) {
case "red":
return const LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
Color(0xFFFFDDDD),
Color(0xFFFFB5B5),
],
);
case "green":
default:
return const LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
Color(0xFFE9FFDD),
Color(0xFFB5FFD1),
],
);
}
}
void showPaymentBottomSheet(BuildContext context) { void showPaymentBottomSheet(BuildContext context) {
showModalBottomSheet( showModalBottomSheet(
...@@ -910,7 +915,6 @@ class _DashboardScreenState extends State<DashboardScreen> { ...@@ -910,7 +915,6 @@ class _DashboardScreenState extends State<DashboardScreen> {
), ),
), ),
), ),
], ],
), ),
), ),
......
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import '../../Utility/AppColors.dart';
import '../../Utility/Reusablewidgets.dart';
class HelpTicketScreen extends StatefulWidget {
final String? reason;
const HelpTicketScreen({super.key, this.reason});
@override
State<HelpTicketScreen> createState() => _HelpTicketScreenState();
}
class _HelpTicketScreenState extends State<HelpTicketScreen> {
final TextEditingController _issueController = TextEditingController();
final TextEditingController _otherReasonController = TextEditingController();
List<String> _selectedImages = [];
String _selectedReason = 'Payment Issue';
@override
void initState() {
super.initState();
if (widget.reason != null) {
_selectedReason = widget.reason!;
}
}
@override
Widget build(BuildContext context) {
final isEditable = widget.reason == null;
final showOtherReasonField = _selectedReason == 'Other Issues';
return SafeArea(
top: false,
child: Scaffold(
backgroundColor: AppColors.backgroundRegular,
appBar: AppBar(
automaticallyImplyLeading: false,
backgroundColor: Colors.white,
elevation: 0,
title: Row(
children: [
InkResponse(
onTap: () => Navigator.pop(context, true),
child: SvgPicture.asset(
"assets/svg/continue_left_ic.svg",
height: 25,
),
),
const SizedBox(width: 10),
const Text(
"Help?",
style: TextStyle(
fontSize: 16,
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w600,
color: Colors.black87,
),
),
],
),
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
/// Section Title
const SectionHeading(title: 'Create New Ticket'),
const SizedBox(height: 12),
/// Reason Label
_fieldLabel("Reason"),
const SizedBox(height: 6),
/// Reason Dropdown
Container(
width: double.infinity,
padding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
decoration: BoxDecoration(
color: Color(0xffE0E0E0),
borderRadius: BorderRadius.circular(12),
),
child: isEditable
? DropdownButtonFormField<String>(
value: _selectedReason,
items: [
'Payment Issue',
'Bill Related Issues',
'Other Issues',
].map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(
value,
style: const TextStyle(
fontSize: 14,
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w500,
color: Colors.black87,
),
),
);
}).toList(),
onChanged: (newValue) {
setState(() {
_selectedReason = newValue!;
});
},
decoration: const InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.zero,
),
)
: Text(
_selectedReason,
style: const TextStyle(
fontSize: 14,
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w500,
color: Colors.black87,
),
),
),
const SizedBox(height: 16),
/// Other Reason Field
if (showOtherReasonField) ...[
_fieldLabel("Enter Reason"),
const SizedBox(height: 6),
_textField(
controller: _otherReasonController,
hint: "Write your reason",
),
const SizedBox(height: 16),
],
/// Issue Description
_fieldLabel("Tell us your issue?"),
const SizedBox(height: 6),
_textField(
controller: _issueController,
hint: "Write your issue",
maxLines: 5,
),
const SizedBox(height: 16),
/// Attachments
_fieldLabel("Add Screenshot (optional)"),
const SizedBox(height: 6),
Container(
width: double.infinity,
padding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Add Image Button
Container(
width: 60,
height: 60,
decoration: BoxDecoration(
color: Colors.grey.shade100,
borderRadius: BorderRadius.circular(8),
),
child: IconButton(
onPressed: _addImage,
icon: Icon(
Icons.add,
size: 24,
color: Colors.grey[600],
),
),
),
const SizedBox(height: 8),
// Selected Images Grid
if (_selectedImages.isNotEmpty)
GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4,
crossAxisSpacing: 8,
mainAxisSpacing: 8,
),
itemCount: _selectedImages.length,
itemBuilder: (context, index) {
return Stack(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Image.network(
_selectedImages[index],
fit: BoxFit.cover,
width: double.infinity,
height: double.infinity,
),
),
Positioned(
top: 4,
right: 4,
child: GestureDetector(
onTap: () => _removeImage(index),
child: Container(
padding: const EdgeInsets.all(2),
decoration: const BoxDecoration(
color: Colors.black54,
shape: BoxShape.circle,
),
child: const Icon(
Icons.close,
size: 14,
color: Colors.white,
),
),
),
),
],
);
},
),
],
),
),
const SizedBox(height: 24),
/// Submit Button
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: _submitTicket,
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.buttonColor,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(28),
),
elevation: 0,
),
child: const Text(
'Submit',
style: TextStyle(
fontSize: 16,
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w600,
),
),
),
),
],
),
),
),
);
}
/// Custom label widget (no shadow, minimal)
Widget _fieldLabel(String text) {
return Text(
text,
style: TextStyle(
fontSize: 12,
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w500,
color: Colors.grey[700],
),
);
}
/// Clean text field (no border or shadow)
Widget _textField({
required TextEditingController controller,
required String hint,
int maxLines = 1,
}) {
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
),
child: TextFormField(
controller: controller,
maxLines: maxLines,
style: const TextStyle(
fontSize: 14,
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w400,
color: Colors.black87,
),
decoration: InputDecoration(
hintText: hint,
hintStyle: TextStyle(
fontSize: 14,
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w400,
color: Colors.grey[400],
),
border: InputBorder.none,
contentPadding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
),
),
);
}
/// Add image (demo)
void _addImage() {
setState(() {
_selectedImages.add('https://via.placeholder.com/100');
});
}
void _removeImage(int index) {
setState(() {
_selectedImages.removeAt(index);
});
}
void _submitTicket() {
final issue = _issueController.text.trim();
final otherReason = _otherReasonController.text.trim();
if (issue.isEmpty) return;
if (_selectedReason == 'Other Issues' && otherReason.isEmpty) return;
print('Submitting ticket with reason: $_selectedReason');
print('Issue: $issue');
}
@override
void dispose() {
_issueController.dispose();
_otherReasonController.dispose();
super.dispose();
}
}
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import '../../Utility/AppColors.dart';
import '../../Utility/Reusablewidgets.dart';
class EnquiryScreen extends StatefulWidget {
const EnquiryScreen({super.key});
@override
State<EnquiryScreen> createState() => _EnquiryScreenState();
}
class _EnquiryScreenState extends State<EnquiryScreen> {
final TextEditingController nameController = TextEditingController();
final TextEditingController emailController = TextEditingController();
final TextEditingController phoneController = TextEditingController();
final TextEditingController requirementController = TextEditingController();
final TextEditingController noteController = TextEditingController();
@override
Widget build(BuildContext context) {
return SafeArea(
top: false,
child: Scaffold(
backgroundColor: AppColors.backgroundRegular,
appBar: AppBar(
automaticallyImplyLeading: false,
backgroundColor: Colors.white,
elevation: 0,
title: Row(
children: [
InkResponse(
onTap: () => Navigator.pop(context, true),
child: SvgPicture.asset(
"assets/svg/continue_left_ic.svg",
height: 25,
),
),
const SizedBox(width: 10),
const Text(
"Enquiry",
style: TextStyle(
fontSize: 16,
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w600,
color: Colors.black87,
),
),
],
),
),
// Main Body
body: SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_labelText("Name"),
_textField(nameController, "Enter Name"),
const SizedBox(height: 16),
_labelText("Email Id"),
_textField(emailController, "Enter Email ID"),
const SizedBox(height: 16),
_labelText("Phone No."),
_textField(phoneController, "Enter Phone Number",
keyboardType: TextInputType.phone),
const SizedBox(height: 16),
_labelText("Requirement"),
_textField(requirementController, "Enter Requirement"),
const SizedBox(height: 16),
_labelText("Note"),
_textField(
noteController,
"Write a short note",
maxLines: 5,
),
const SizedBox(height: 32),
// Submit button
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: () {
// Submit action
FocusScope.of(context).unfocus();
},
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.buttonColor,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(28),
),
elevation: 0,
),
child: const Text(
"Submit",
style: TextStyle(
fontSize: 16,
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w600,
),
),
),
),
const SizedBox(height: 16),
],
),
),
),
);
}
/// Label Text (like "Name", "Email Id")
Widget _labelText(String title) {
return Text(
title,
style: const TextStyle(
fontSize: 13,
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w500,
color: Colors.black,
),
);
}
/// Rounded Input Field
Widget _textField(
TextEditingController controller,
String hint, {
TextInputType keyboardType = TextInputType.text,
int maxLines = 1,
}) {
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
),
child: TextFormField(
controller: controller,
keyboardType: keyboardType,
maxLines: maxLines,
decoration: InputDecoration(
hintText: hint,
hintStyle: TextStyle(
fontSize: 14,
color: Colors.grey[400],
fontFamily: "Plus Jakarta Sans",
),
contentPadding:
const EdgeInsets.symmetric(horizontal: 12, vertical: 14),
border: InputBorder.none,
),
),
);
}
@override
void dispose() {
nameController.dispose();
emailController.dispose();
phoneController.dispose();
requirementController.dispose();
noteController.dispose();
super.dispose();
}
}
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:gen_rentals/Screens/HelpScreens/CreateTicketScreen.dart';
import 'package:gen_rentals/Screens/HelpScreens/ProcessTicketScreen.dart';
import 'package:gen_rentals/Utility/Reusablewidgets.dart';
import '../../Utility/AppColors.dart';
class HelpScreen extends StatefulWidget {
const HelpScreen({super.key});
@override
State<HelpScreen> createState() => _HelpScreenState();
}
class _HelpScreenState extends State<HelpScreen> {
// Dummy data for help - with proper null safety
final List<Map<String, dynamic>> createNewTickets = [
{
'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),
},
];
final List<Map<String, String>> processingTickets = [
{'title': 'Payment Issue', 'date': '25th Jan 2025', 'status': 'In Process'},
];
final List<Map<String, String>> closedTickets = [
{'title': 'Bill Payments', 'date': '25th Jan 2025'},
{'title': 'Others', 'date': '25th Jan 2025'},
{'title': 'Payment Issue', 'date': '25th Jan 2025'},
];
@override
Widget build(BuildContext context) {
return SafeArea(
top: false,
child: Scaffold(
backgroundColor: AppColors.backgroundRegular,
appBar: AppBar(
automaticallyImplyLeading: false,
backgroundColor: Colors.white,
title: Row(
children: [
InkResponse(
onTap: () => Navigator.pop(context, true),
child: SvgPicture.asset(
"assets/svg/continue_left_ic.svg",
height: 25,
),
),
const SizedBox(width: 10),
Text(
"Help?",
style: TextStyle(
fontSize: 16,
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w600,
color: Colors.black87,
),
),
],
),
),
// Main content
body: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Create New Ticket Section
SectionHeading(
title: 'Create New Ticket',
padding: EdgeInsets.symmetric(horizontal: 2, vertical: 4),
),
const SizedBox(height: 12),
_buildCreateNewTicketSection(),
const SizedBox(height: 12),
// Processing Tickets Section
SectionHeading(
title: 'Processing Tickets',
padding: EdgeInsets.symmetric(horizontal: 2, vertical: 4),
),
const SizedBox(height: 2),
_buildProcessingTicketsSection(),
const SizedBox(height: 10),
// Closed Tickets Section
SectionHeading(
title: 'Closed Tickets',
padding: EdgeInsets.symmetric(horizontal: 2, vertical: 4),
),
const SizedBox(height: 2),
_buildClosedTicketsSection(),
],
),
),
),
);
}
Widget _buildCreateNewTicketSection() {
return GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
crossAxisSpacing: 12,
mainAxisSpacing: 12,
childAspectRatio: 0.99,
),
itemCount: createNewTickets.length,
itemBuilder: (context, index) {
final ticket = createNewTickets[index];
final String title = ticket['title'] ?? 'Unknown';
final String description = ticket['description'] ?? '';
final String icon = ticket['icon'] ?? 'assets/svg/help_ic.svg';
final Color color = ticket['color'] ?? Colors.grey;
return _buildFeatureCard(
title: title,
description: description,
icon: icon,
color: color,
);
},
);
}
Widget _buildFeatureCard({
required String title,
required String description,
required String icon,
required Color color,
}) {
return GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => HelpTicketScreen(reason: title,))
);
},
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 2, vertical: 1),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// Icon container
Container(
width: 88,
height: 88,
decoration: BoxDecoration(
color: color.withOpacity(0.7),
borderRadius: BorderRadius.circular(12),
),
child: Center(
child: SizedBox(
height: 40,
width: 40,
child: SvgPicture.asset(
icon,
fit: BoxFit.fitWidth,
),
),
),
),
const SizedBox(height: 8),
// Title
SizedBox(
child: Text(
title,
textAlign: TextAlign.center,
style: TextStyle(
color: AppColors.nearDarkText,
fontSize: 14,
fontWeight: FontWeight.w400,
fontFamily: "Plus Jakarta Sans",
),
),
),
const SizedBox(height: 4),
],
),
),
);
}
Widget _buildProcessingTicketsSection() {
return Container(
padding: const EdgeInsets.all(4),
child: Column(
children: processingTickets.map((ticket) {
return Padding(
padding: const EdgeInsets.only(bottom: 4),
child: CommonListItem(
title: ticket['title']!,
date: ticket['date']!,
status: ticket['status']!,
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => ProcessTicketChatScreen())
);
},
),
);
}).toList(),
),
);
}
Widget _buildClosedTicketsSection() {
return Container(
padding: const EdgeInsets.all(4),
child: Column(
children: closedTickets.map((ticket) {
return Padding(
padding: const EdgeInsets.only(bottom: 4),
child: CommonListItem(
title: ticket['title']!,
date: ticket['date']!,
status: "", // Empty status for closed tickets
onTap: () {
// Handle closed ticket tap
},
),
);
}).toList(),
),
);
}
}
class CommonListItem extends StatelessWidget {
final String title;
final String date;
final String status;
final VoidCallback? onTap;
const CommonListItem({
Key? key,
required this.title,
required this.date,
required this.status,
this.onTap,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Column(
children: [
const SizedBox(height: 4),
Material(
color: Colors.transparent,
child: InkWell(
borderRadius: BorderRadius.circular(12),
onTap: onTap,
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
),
padding: const EdgeInsets.symmetric(vertical: 14, horizontal: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(
fontSize: 14,
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w500,
color: Colors.black87,
),
),
const SizedBox(height: 4),
Text(
date,
style: TextStyle(
fontSize: 12,
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w400,
color: Colors.grey[600],
),
),
],
),
),
if (status.isNotEmpty)
Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: Colors.green.withOpacity(0.2),
borderRadius: BorderRadius.circular(6),
),
child: Text(
status,
style: const TextStyle(
fontSize: 12,
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w500,
color: Colors.black87,
),
),
),
],
),
),
),
),
],
);
}
}
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import '../../Utility/AppColors.dart';
import '../../Utility/Reusablewidgets.dart';
class ProcessTicketChatScreen extends StatefulWidget {
final String? reason;
const ProcessTicketChatScreen({super.key, this.reason});
@override
State<ProcessTicketChatScreen> createState() =>
_ProcessTicketChatScreenState();
}
class _ProcessTicketChatScreenState extends State<ProcessTicketChatScreen> {
final TextEditingController _messageController = TextEditingController();
List<String> _selectedImages = [];
@override
Widget build(BuildContext context) {
return SafeArea(
top: false,
child: Scaffold(
backgroundColor: AppColors.backgroundRegular,
appBar: AppBar(
automaticallyImplyLeading: false,
backgroundColor: Colors.white,
elevation: 0,
title: Row(
children: [
InkResponse(
onTap: () => Navigator.pop(context, true),
child: SvgPicture.asset(
"assets/svg/continue_left_ic.svg",
height: 25,
),
),
const SizedBox(width: 10),
const Text(
"Help?",
style: TextStyle(
fontSize: 16,
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w600,
color: Colors.black87,
),
),
const Spacer(),
Container(
padding:
const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
decoration: BoxDecoration(
color: const Color(0xFFD7F7D9),
borderRadius: BorderRadius.circular(16),
),
child: const Text(
"In Process",
style: TextStyle(
fontSize: 12,
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w500,
color: Colors.green,
),
),
),
],
),
),
// Chat Messages + Button
body: Column(
children: [
Expanded(
child: SingleChildScrollView(
padding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 4,),
/// USER MESSAGE
_chatBubble(
name: "Rajveer Singh",
date: "25th Jan 2025",
message:
"I am writing to express my dissatisfaction regarding an issue with my order set for January 25, 2025. It’s frustrating to encounter problems like this, and I would appreciate prompt assistance from your support team to resolve this matter.",
isUser: true,
),
const SizedBox(height: 16),
/// SUPPORT MESSAGE
_chatBubble(
name: "Genrental Support",
date: "25th Jan 2025",
message:
"We regret to inform you that there is a problem with your order scheduled for January 25, 2025. Please contact our support team for assistance.",
isUser: false,
),
],
),
),
),
/// Bottom Button
Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
color: Colors.white,
child: ElevatedButton(
onPressed: _openMessageSheet,
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.buttonColor,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(28),
),
elevation: 0,
),
child: const Text(
"Send a Message",
style: TextStyle(
fontSize: 16,
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w600,
),
),
),
),
],
),
),
);
}
/// Chat bubble widget
Widget _chatBubble({
required String name,
required String date,
required String message,
required bool isUser,
}) {
return Container(
width: double.infinity,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Profile Icon
CircleAvatar(
radius: 16,
backgroundColor: isUser ? Colors.blue.shade200 : Colors.blue,
child: Text(
name[0],
style: const TextStyle(
color: Colors.white,
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
),
const SizedBox(width: 10),
// Message Content
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
name,
style: const TextStyle(
fontSize: 14,
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w600,
color: Colors.black,
),
),
const SizedBox(height: 2),
Text(
date,
style: TextStyle(
fontSize: 12,
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w400,
color: Colors.grey[600],
),
),
const SizedBox(height: 6),
Text(
message,
style: const TextStyle(
fontSize: 13,
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w400,
color: Colors.black87,
height: 1.5,
),
),
const SizedBox(height: 10),
Container(
height: 1,
width: double.infinity,
color: Colors.grey.shade300,
),
],
),
),
],
),
);
}
/// Bottom Sheet for sending message
void _openMessageSheet() {
showModalBottomSheet(
isScrollControlled: true,
backgroundColor: Colors.white,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(24)),
),
context: context,
builder: (context) {
return Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
top: 16,
left: 16,
right: 16,
),
child: StatefulBuilder(
builder: (context, setSheetState) {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Center(
child: Container(
width: 40,
height: 4,
decoration: BoxDecoration(
color: Colors.grey[300],
borderRadius: BorderRadius.circular(4),
),
),
),
const SizedBox(height: 12),
const Center(
child: Text(
"Send a Message",
style: TextStyle(
fontSize: 16,
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w600,
color: Colors.black,
),
),
),
const SizedBox(height: 16),
Text(
"Message",
style: TextStyle(
fontSize: 12,
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w500,
color: Colors.grey[700],
),
),
const SizedBox(height: 6),
Container(
decoration: BoxDecoration(
color: Colors.grey.shade100,
borderRadius: BorderRadius.circular(12),
),
child: TextFormField(
controller: _messageController,
maxLines: 5,
decoration: InputDecoration(
hintText: "Write your message",
hintStyle: TextStyle(
fontSize: 14,
color: Colors.grey[400],
fontFamily: "Plus Jakarta Sans",
),
border: InputBorder.none,
contentPadding: const EdgeInsets.all(12),
),
),
),
const SizedBox(height: 16),
Text(
"Attach Screenshot (optional)",
style: TextStyle(
fontSize: 12,
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w500,
color: Colors.grey[700],
),
),
const SizedBox(height: 8),
Wrap(
spacing: 8,
runSpacing: 8,
children: [
GestureDetector(
onTap: () {
setSheetState(() {
_selectedImages
.add('https://via.placeholder.com/100');
});
},
child: Container(
width: 60,
height: 60,
decoration: BoxDecoration(
color: Colors.grey.shade100,
borderRadius: BorderRadius.circular(8),
),
child: Icon(
Icons.add,
color: Colors.grey[600],
),
),
),
..._selectedImages.map(
(img) => Stack(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Image.network(
img,
width: 60,
height: 60,
fit: BoxFit.cover,
),
),
Positioned(
top: 2,
right: 2,
child: GestureDetector(
onTap: () {
setSheetState(() {
_selectedImages.remove(img);
});
},
child: Container(
decoration: const BoxDecoration(
color: Colors.black54,
shape: BoxShape.circle,
),
child: const Icon(
Icons.close,
size: 14,
color: Colors.white,
),
),
),
),
],
),
),
],
),
const SizedBox(height: 24),
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: () {
Navigator.pop(context);
_messageController.clear();
},
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.buttonColor,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(28),
),
elevation: 0,
),
child: const Text(
"Send Message",
style: TextStyle(
fontSize: 16,
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w600,
),
),
),
),
const SizedBox(height: 16),
],
);
},
),
);
},
);
}
@override
void dispose() {
_messageController.dispose();
super.dispose();
}
}
...@@ -8,6 +8,7 @@ import 'package:gen_rentals/Screens/DashboardScreen.dart'; ...@@ -8,6 +8,7 @@ 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/TransactionsScreen.dart';
import '../Utility/CustomSnackbar.dart'; import '../Utility/CustomSnackbar.dart';
import '../Utility/SharedpreferencesService.dart';
import 'authScreen/LoginScreen.dart'; import 'authScreen/LoginScreen.dart';
class SplashScreen extends StatefulWidget { class SplashScreen extends StatefulWidget {
...@@ -24,11 +25,12 @@ class _SplashScreenState extends State<SplashScreen> with SingleTickerProviderSt ...@@ -24,11 +25,12 @@ class _SplashScreenState extends State<SplashScreen> with SingleTickerProviderSt
late Animation<Offset> _slideAnimation; late Animation<Offset> _slideAnimation;
late Animation<double> _rotationAnimation; late Animation<double> _rotationAnimation;
final prefs = SharedPreferencesService.instance;
Timer? _connectivityTimer; Timer? _connectivityTimer;
bool _progressCheckCompleted = false; bool _progressCheckCompleted = false;
bool _hasInternet = true; bool _hasInternet = true;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
...@@ -77,10 +79,38 @@ class _SplashScreenState extends State<SplashScreen> with SingleTickerProviderSt ...@@ -77,10 +79,38 @@ class _SplashScreenState extends State<SplashScreen> with SingleTickerProviderSt
// Navigate after delay // Navigate after delay
Timer(const Duration(seconds: 3), () { Timer(const Duration(seconds: 3), () {
_checkAccountAndNavigate();
});
}
Future<void> _checkAccountAndNavigate() async {
try {
// Check if account ID exists in shared preferences
final String? savedAccId = await prefs.getString("accId");
if (savedAccId != null && savedAccId.isNotEmpty) {
// Account ID exists, navigate directly to Dashboard
_navigateToDashboard();
} else {
// No account ID found, navigate to Login
_navigateToLogin();
}
} catch (e) {
debugPrint("Error checking account ID: $e");
// Fallback to login screen on error
_navigateToLogin();
}
}
Future<void> _navigateToDashboard() async {
final String? savedAccId = await prefs.getString("accId");
// Check if accId is not null and not empty
if (savedAccId != null && savedAccId.isNotEmpty) {
Navigator.pushReplacement( Navigator.pushReplacement(
context, context,
PageRouteBuilder( PageRouteBuilder(
pageBuilder: (_, __, ___) => LoginScreen(), pageBuilder: (_, __, ___) => DashboardScreen(accId: savedAccId),
transitionsBuilder: (_, animation, __, child) { transitionsBuilder: (_, animation, __, child) {
return FadeTransition( return FadeTransition(
opacity: animation, opacity: animation,
...@@ -90,9 +120,27 @@ class _SplashScreenState extends State<SplashScreen> with SingleTickerProviderSt ...@@ -90,9 +120,27 @@ class _SplashScreenState extends State<SplashScreen> with SingleTickerProviderSt
transitionDuration: const Duration(milliseconds: 800), transitionDuration: const Duration(milliseconds: 800),
), ),
); );
}); } else {
// If no valid accId, navigate to login
_navigateToLogin();
}
} }
void _navigateToLogin() {
Navigator.pushReplacement(
context,
PageRouteBuilder(
pageBuilder: (_, __, ___) => const LoginScreen(),
transitionsBuilder: (_, animation, __, child) {
return FadeTransition(
opacity: animation,
child: child,
);
},
transitionDuration: const Duration(milliseconds: 800),
),
);
}
Future<void> _initConnectivity() async { Future<void> _initConnectivity() async {
try { try {
...@@ -146,10 +194,6 @@ class _SplashScreenState extends State<SplashScreen> with SingleTickerProviderSt ...@@ -146,10 +194,6 @@ class _SplashScreenState extends State<SplashScreen> with SingleTickerProviderSt
} else { } else {
// Dismiss the warning snackbar if internet is restored // Dismiss the warning snackbar if internet is restored
ScaffoldMessenger.of(context).hideCurrentSnackBar(); ScaffoldMessenger.of(context).hideCurrentSnackBar();
// if (!_progressCheckCompleted) {
// _startLoginCheck();
// }
} }
} }
...@@ -165,20 +209,18 @@ class _SplashScreenState extends State<SplashScreen> with SingleTickerProviderSt ...@@ -165,20 +209,18 @@ class _SplashScreenState extends State<SplashScreen> with SingleTickerProviderSt
action: SnackBarAction( action: SnackBarAction(
label: "RETRY", label: "RETRY",
onPressed: () { onPressed: () {
// Retry logic _checkConnectivity();
}, },
), ),
); );
}); });
} }
} }
@override @override
void dispose() { void dispose() {
_controller.dispose(); _controller.dispose();
_connectivityTimer?.cancel();
super.dispose(); super.dispose();
} }
......
...@@ -64,27 +64,25 @@ class _TransactionsScreenState extends State<TransactionsScreen> { ...@@ -64,27 +64,25 @@ class _TransactionsScreenState extends State<TransactionsScreen> {
child: Scaffold( child: Scaffold(
backgroundColor: AppColors.backgroundRegular, backgroundColor: AppColors.backgroundRegular,
appBar: AppBar( appBar: AppBar(
automaticallyImplyLeading: false,
backgroundColor: Colors.white, backgroundColor: Colors.white,
elevation: 0,
titleSpacing: 0,
title: Row( title: Row(
children: [ children: [
InkResponse( InkResponse(
onTap: () => Navigator.pop(context), onTap: () => Navigator.pop(context, true),
child: SvgPicture.asset( child: SvgPicture.asset(
"assets/svg/continue_left_ic.svg", "assets/svg/continue_left_ic.svg",
height: 25, height: 25
width: 25,
), ),
), ),
const SizedBox(width: 12), const SizedBox(width: 10),
const Text( Text(
"Transactions", "Transactions",
style: TextStyle( style: TextStyle(
fontSize: 16, fontSize: 16,
fontFamily: "Poppins", fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w500, fontWeight: FontWeight.w600,
color: Colors.black, color: Colors.black87,
), ),
), ),
], ],
......
...@@ -5,6 +5,7 @@ import 'package:provider/provider.dart'; ...@@ -5,6 +5,7 @@ import 'package:provider/provider.dart';
import '../../Notifier/RentalContactProvider .dart'; import '../../Notifier/RentalContactProvider .dart';
import '../../Utility/AdvancedSnackbar.dart'; import '../../Utility/AdvancedSnackbar.dart';
import '../../Utility/CustomSnackbar.dart'; import '../../Utility/CustomSnackbar.dart';
import '../../Utility/SharedpreferencesService.dart';
class LoginScreen extends StatefulWidget { class LoginScreen extends StatefulWidget {
const LoginScreen({super.key}); const LoginScreen({super.key});
...@@ -18,6 +19,8 @@ class _LoginScreenState extends State<LoginScreen> { ...@@ -18,6 +19,8 @@ class _LoginScreenState extends State<LoginScreen> {
bool _isValid = false; bool _isValid = false;
bool _isDirty = false; bool _isDirty = false;
final prefs = SharedPreferencesService.instance;
void _validatePhone(String value) { void _validatePhone(String value) {
setState(() { setState(() {
_isDirty = true; _isDirty = true;
...@@ -39,8 +42,9 @@ class _LoginScreenState extends State<LoginScreen> { ...@@ -39,8 +42,9 @@ class _LoginScreenState extends State<LoginScreen> {
message: rentalProvider.response?.errorMsg ?? "OTP sent to your registered mobile number!", message: rentalProvider.response?.errorMsg ?? "OTP sent to your registered mobile number!",
); );
// Navigate to OTP screen after a short delay // Navigate to OTP screen after a short delay
Future.delayed(const Duration(milliseconds: 1500), () { Future.delayed(const Duration(milliseconds: 500), () {
if (mounted) { if (mounted) {
Navigator.pushReplacement( Navigator.pushReplacement(
context, context,
...@@ -55,7 +59,7 @@ class _LoginScreenState extends State<LoginScreen> { ...@@ -55,7 +59,7 @@ class _LoginScreenState extends State<LoginScreen> {
CustomSnackBar.showWarning( CustomSnackBar.showWarning(
context: context, context: context,
title: "Login Failed", title: "Login Failed",
message: rentalProvider.response?.errorMsg ?? message: rentalProvider.response?.errorMsg.toString() ??
"Mobile number not registered or invalid", "Mobile number not registered or invalid",
); );
} }
......
...@@ -5,6 +5,7 @@ import 'package:provider/provider.dart'; ...@@ -5,6 +5,7 @@ import 'package:provider/provider.dart';
import '../../Notifier/RentalContactProvider .dart'; import '../../Notifier/RentalContactProvider .dart';
import '../../Utility/AdvancedSnackbar.dart'; import '../../Utility/AdvancedSnackbar.dart';
import '../../Utility/CustomSnackbar.dart'; import '../../Utility/CustomSnackbar.dart';
import '../../Utility/SharedpreferencesService.dart';
class OtpScreen extends StatefulWidget { class OtpScreen extends StatefulWidget {
final String mob; // phone number final String mob; // phone number
...@@ -19,6 +20,7 @@ class OtpScreen extends StatefulWidget { ...@@ -19,6 +20,7 @@ class OtpScreen extends StatefulWidget {
} }
class _OtpScreenState extends State<OtpScreen> { class _OtpScreenState extends State<OtpScreen> {
final prefs = SharedPreferencesService.instance;
final List<TextEditingController> _controllers = final List<TextEditingController> _controllers =
List.generate(4, (_) => TextEditingController()); List.generate(4, (_) => TextEditingController());
final List<FocusNode> _focusNodes = List.generate(4, (_) => FocusNode()); final List<FocusNode> _focusNodes = List.generate(4, (_) => FocusNode());
...@@ -71,6 +73,7 @@ class _OtpScreenState extends State<OtpScreen> { ...@@ -71,6 +73,7 @@ class _OtpScreenState extends State<OtpScreen> {
setState(() => _isVerifying = true); setState(() => _isVerifying = true);
try { try {
// Remove .toString() since enteredOtp is already a string
await rentalProvider.fetchMobileOtp(widget.mob, enteredOtp); await rentalProvider.fetchMobileOtp(widget.mob, enteredOtp);
if (rentalProvider.otpResponse != null) { if (rentalProvider.otpResponse != null) {
...@@ -84,10 +87,16 @@ class _OtpScreenState extends State<OtpScreen> { ...@@ -84,10 +87,16 @@ class _OtpScreenState extends State<OtpScreen> {
message: rentalProvider.otpResponse?.message ?? "OTP Verified Successfully!", message: rentalProvider.otpResponse?.message ?? "OTP Verified Successfully!",
); );
// Saving staff id after verification
// Added null check for accId
if (rentalProvider.otpResponse?.accId != null) {
await prefs.saveString("accId", rentalProvider.otpResponse!.accId!);
}
// Navigate to dashboard // Navigate to dashboard
Navigator.pushReplacement( Navigator.pushReplacement(
context, context,
MaterialPageRoute(builder: (_) => const DashboardScreen()), MaterialPageRoute(builder: (_) => DashboardScreen(accId: rentalProvider.otpResponse!.accId!,)),
); );
} else { } else {
// ❌ Invalid OTP // ❌ Invalid OTP
...@@ -100,11 +109,11 @@ class _OtpScreenState extends State<OtpScreen> { ...@@ -100,11 +109,11 @@ class _OtpScreenState extends State<OtpScreen> {
); );
} }
} else { } else {
// ❌ API Error // ❌ API Error - Response is null
CustomSnackBar.showError( CustomSnackBar.showError(
context: context, context: context,
title: "Error", title: "Error",
message: rentalProvider.otpResponse?.message ?? "Failed to verify OTP. Please try again.", message: "Failed to verify OTP. Please try again.",
); );
} }
} catch (e) { } catch (e) {
......
...@@ -35,4 +35,4 @@ const ticketListUrl = "${baseUrl}ticket_list"; ...@@ -35,4 +35,4 @@ const ticketListUrl = "${baseUrl}ticket_list";
/// dashboard and login /// dashboard and login
const fetchMobileUrl = "${baseUrl}fetch_mobile_number"; const fetchMobileUrl = "${baseUrl}fetch_mobile_number";
const fetchOtpUrl = "${baseUrl}login"; const fetchOtpUrl = "${baseUrl}login";
const dashboardUrl = "${baseUrl}ticket_list"; const dashboardUrl = "${baseUrl}dashboard";
\ No newline at end of file \ No newline at end of file
...@@ -534,18 +534,18 @@ class ApiCalling { ...@@ -534,18 +534,18 @@ class ApiCalling {
/// Fetch Dashboard /// Fetch Dashboard
static Future<DashboardResponse?> fetchDashboardApi( static Future<DashboardResponse?> fetchDashboardApi(
String sessionId, String accId,
String empId,
String mob,
) async { ) async {
debugPrint("Dashboard Api called ##############");
try { try {
Map<String, String> data = { Map<String, String> data = {
"session_id": sessionId, "acc_id": accId,
"emp_id": empId,
"mob": mob,
}; };
debugPrint("Account Id : $accId");
final res = await post(data, dashboardUrl, {}); final res = await post(data, dashboardUrl, {});
debugPrint(res?.body);
if (res != null) { if (res != null) {
return DashboardResponse.fromJson(jsonDecode(res.body)); return DashboardResponse.fromJson(jsonDecode(res.body));
......
import 'package:shared_preferences/shared_preferences.dart';
class SharedPreferencesService {
// Singleton
SharedPreferencesService._privateConstructor();
static final SharedPreferencesService instance =
SharedPreferencesService._privateConstructor();
/// Save String
Future<bool> saveString(String key, String value) async {
final prefs = await SharedPreferences.getInstance();
return prefs.setString(key, value);
}
/// Save Int
Future<bool> saveInt(String key, int value) async {
final prefs = await SharedPreferences.getInstance();
return prefs.setInt(key, value);
}
/// Save Bool
Future<bool> saveBool(String key, bool value) async {
final prefs = await SharedPreferences.getInstance();
return prefs.setBool(key, value);
}
/// Save Double
Future<bool> saveDouble(String key, double value) async {
final prefs = await SharedPreferences.getInstance();
return prefs.setDouble(key, value);
}
/// Get String
Future<String?> getString(String key) async {
final prefs = await SharedPreferences.getInstance();
return prefs.getString(key);
}
/// Get Int
Future<int?> getInt(String key) async {
final prefs = await SharedPreferences.getInstance();
return prefs.getInt(key);
}
/// Get Bool
Future<bool?> getBool(String key) async {
final prefs = await SharedPreferences.getInstance();
return prefs.getBool(key);
}
/// Get Double
Future<double?> getDouble(String key) async {
final prefs = await SharedPreferences.getInstance();
return prefs.getDouble(key);
}
/// Remove a key
Future<bool> remove(String key) async {
final prefs = await SharedPreferences.getInstance();
return prefs.remove(key);
}
/// Clear all preferences
Future<bool> clearPreferences() async {
final prefs = await SharedPreferences.getInstance();
return prefs.clear();
}
}
...@@ -133,10 +133,10 @@ packages: ...@@ -133,10 +133,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:
...@@ -284,26 +284,26 @@ packages: ...@@ -284,26 +284,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:
...@@ -705,10 +705,10 @@ packages: ...@@ -705,10 +705,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:
...@@ -753,10 +753,10 @@ packages: ...@@ -753,10 +753,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:
...@@ -798,5 +798,5 @@ packages: ...@@ -798,5 +798,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"
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