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 {
applicationId = "in.webgrid.genrentals"
// You can update the following values to match your application needs.
// For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk = 23
minSdk = flutter.minSdkVersion
targetSdk = 36
versionCode = flutter.versionCode
versionName = flutter.versionName
......
class DashboardResponse {
int? error;
String? message;
int? exist;
String? city;
String? error;
String? raname;
String? mob;
String? mail;
String? address;
String? state;
String? accId;
int? otp;
String? balanceAmount;
String? message;
List<Products>? products;
DashboardResponse(
{this.error,
this.message,
this.exist,
this.city,
this.raname,
this.mob,
this.mail,
this.address,
this.state,
this.accId,
this.otp,
this.balanceAmount,
this.message,
this.products});
DashboardResponse.fromJson(Map<String, dynamic> json) {
error = json['error'];
message = json['message'];
exist = json['exist'];
city = json['city'];
raname = json['raname'];
mob = json['mob'];
mail = json['mail'];
address = json['address'];
state = json['state'];
accId = json['acc_id'];
otp = json['otp'];
balanceAmount = json['balance_amount'];
message = json['message'];
if (json['products'] != null) {
products = <Products>[];
json['products'].forEach((v) {
......@@ -49,16 +28,9 @@ class DashboardResponse {
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['error'] = this.error;
data['message'] = this.message;
data['exist'] = this.exist;
data['city'] = this.city;
data['raname'] = this.raname;
data['mob'] = this.mob;
data['mail'] = this.mail;
data['address'] = this.address;
data['state'] = this.state;
data['acc_id'] = this.accId;
data['otp'] = this.otp;
data['balance_amount'] = this.balanceAmount;
data['message'] = this.message;
if (this.products != null) {
data['products'] = this.products!.map((v) => v.toJson()).toList();
}
......
......@@ -14,9 +14,7 @@ class DashboardProvider with ChangeNotifier {
/// Fetch Dashboard API
Future<void> fetchDashboard({
required String sessionId,
required String empId,
required String mob,
required String accId,
}) async {
_isLoading = true;
_errorMessage = null;
......@@ -24,7 +22,7 @@ class DashboardProvider with ChangeNotifier {
try {
final response =
await ApiCalling.fetchDashboardApi(sessionId, empId, mob);
await ApiCalling.fetchDashboardApi(accId,);
if (response != null) {
_dashboardData = response;
......
import 'package:flutter/material.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/TransactionsScreen.dart';
import 'package:provider/provider.dart';
import '../Models/DashboardResponse.dart';
import '../Notifier/DashboardProvider.dart';
import 'authScreen/LoginScreen.dart';
class DashboardScreen extends StatefulWidget {
const DashboardScreen({super.key});
final String accId;
const DashboardScreen({super.key, required this.accId});
@override
State<DashboardScreen> createState() => _DashboardScreenState();
}
class _DashboardScreenState extends State<DashboardScreen> {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
Provider.of<DashboardProvider>(context, listen: false).fetchDashboard(
sessionId: "4d21382d9e1c4d6e0b7c426d53d89b6b7d48078877f185289092e6fa13bac4b11d417d37738b20b34151b8e638625b3ec013",
empId: '5',
mob: '9876543210',
// Only fetch dashboard if accId is valid
if (widget.accId.isNotEmpty && widget.accId != "null") {
Provider.of<DashboardProvider>(context, listen: false).fetchDashboard(accId: widget.accId);
} else {
// If accId is invalid, navigate back to login
WidgetsBinding.instance.addPostFrameCallback((_) {
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => LoginScreen()),
);
});
}
});
}
@override
Widget build(BuildContext context) {
final dashboardProvider = Provider.of<DashboardProvider>(context);
final dashboardData = dashboardProvider.dashboardData;
double screenWidth = MediaQuery.of(context).size.width;
double screenHeight = MediaQuery.of(context).size.height;
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(
top: false,
child: Scaffold(
......@@ -131,9 +162,10 @@ class _DashboardScreenState extends State<DashboardScreen> {
fontWeight: FontWeight.w500,
),
),
const Text(
"Mohit Kumar",
style: TextStyle(
// Use provider data for name
Text(
dashboardData?.raname ?? "Loading...",
style: const TextStyle(
fontFamily: "Poppins",
color: Colors.grey,
fontSize: 16,
......@@ -188,9 +220,10 @@ class _DashboardScreenState extends State<DashboardScreen> {
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
"₹24,800",
style: TextStyle(
// Use provider data for balance amount - FIXED NULL CHECK
Text(
dashboardData?.balanceAmount ?? "0",
style: const TextStyle(
fontFamily: "Poppins",
color: Colors.black,
fontSize: 32,
......@@ -219,7 +252,6 @@ class _DashboardScreenState extends State<DashboardScreen> {
color: Colors.grey.shade500,
fontSize: 12,
fontWeight: FontWeight.w400,
// fontStyle: FontStyle.italic,
),
),
],
......@@ -238,6 +270,14 @@ class _DashboardScreenState extends State<DashboardScreen> {
// Big card - Any Requirements
Expanded(
flex: 2,
child: InkResponse(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => EnquiryScreen()
)
);
},
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10),
decoration: BoxDecoration(
......@@ -247,13 +287,6 @@ class _DashboardScreenState extends State<DashboardScreen> {
color: Colors.grey.shade200,
width: 1,
),
// boxShadow: [
// BoxShadow(
// color: Colors.black.withOpacity(0.05),
// blurRadius: 8,
// offset: const Offset(0, 2),
// ),
// ],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
......@@ -262,7 +295,6 @@ class _DashboardScreenState extends State<DashboardScreen> {
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 16),
Text(
"Any \nRequirements ?",
......@@ -299,6 +331,7 @@ class _DashboardScreenState extends State<DashboardScreen> {
),
),
),
),
const SizedBox(width: 10),
......@@ -318,13 +351,6 @@ class _DashboardScreenState extends State<DashboardScreen> {
color: Colors.grey.shade200,
width: 1,
),
// boxShadow: [
// BoxShadow(
// color: Colors.black.withOpacity(0.05),
// blurRadius: 8,
// offset: const Offset(0, 2),
// ),
// ],
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
......@@ -332,16 +358,22 @@ class _DashboardScreenState extends State<DashboardScreen> {
children: [
Expanded(
flex: 5,
child: InkResponse(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => HelpScreen()),
);
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
flex: 5,
child: Text(
"Have Complaints?",
"Need Help?",
style: TextStyle(
fontFamily: "Poppins",
color: Color(0xFF008CDE),
......@@ -350,7 +382,6 @@ class _DashboardScreenState extends State<DashboardScreen> {
),
),
),
Expanded(
flex: 2,
child: SvgPicture.asset(
......@@ -371,10 +402,10 @@ class _DashboardScreenState extends State<DashboardScreen> {
fontSize: 12,
),
),
],
),
),
),
],
),
),
......@@ -399,13 +430,6 @@ class _DashboardScreenState extends State<DashboardScreen> {
color: Colors.grey.shade200,
width: 1,
),
// boxShadow: [
// BoxShadow(
// color: Colors.black.withOpacity(0.05),
// blurRadius: 8,
// offset: const Offset(0, 2),
// ),
// ],
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
......@@ -421,7 +445,7 @@ class _DashboardScreenState extends State<DashboardScreen> {
Expanded(
flex: 5,
child: Text(
"Know Your Payments!",
"Transactions!",
style: TextStyle(
fontFamily: "Poppins",
color: Color(0xFF008CDE),
......@@ -442,8 +466,7 @@ class _DashboardScreenState extends State<DashboardScreen> {
),
const SizedBox(height: 4),
Text(
"View your all transactions with us."
"",
"View your all transactions with us.",
style: TextStyle(
fontFamily: "Poppins",
color: Colors.grey.shade600,
......@@ -451,11 +474,9 @@ class _DashboardScreenState extends State<DashboardScreen> {
fontSize: 12,
),
),
],
),
),
],
),
),
......@@ -487,10 +508,26 @@ class _DashboardScreenState extends State<DashboardScreen> {
),
const SizedBox(height: 16),
// List of subscribed products
// Show loading or products list
if (dashboardProvider.isLoading && dashboardData == null)
const Center(
child: CircularProgressIndicator(),
)
else if (dashboardData?.products == null || dashboardData!.products!.isEmpty)
const Text(
"No products subscribed",
style: TextStyle(
fontFamily: "Poppins",
color: Colors.grey,
fontSize: 14,
),
)
else
// List of subscribed products from API
Column(
children: dashboardData!.products!.map((product) {
return Column(
children: [
// Item 1 - Expiring in 9 Days
InkResponse(
onTap: () {
Navigator.push(
......@@ -498,42 +535,12 @@ class _DashboardScreenState extends State<DashboardScreen> {
MaterialPageRoute(builder: (context) => ProductsDetailScreen()),
);
},
child: _buildProductItem(
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,
child: _buildProductItemFromApi(product),
),
),
const SizedBox(height: 16),
// Item 2 - With Pending Payment
_buildProductItem(
productName: "Genesis 85kVA",
plan: "3999",
rentedDate: "Rented on 7th July, 2025",
timeLeft: "2 ",
expiringText: "months left",
pendingPaymentText: "1 Payment Pending. Please Pay before incurring fines.",
hasPendingPayment: true,
),
const SizedBox(height: 16),
// Item 3 - Normal
_buildProductItem(
productName: "Genesis 30kVA",
plan: "3999",
rentedDate: "Rented on 7th July, 2025",
timeLeft: "7 ",//image url and color of timeleft
expiringText: "months left",
hasPendingPayment: false,
),
],
);
}).toList(),
),
],
),
......@@ -553,22 +560,14 @@ class _DashboardScreenState extends State<DashboardScreen> {
);
}
// Helper widget for product item
Widget _buildProductItem({
required String productName,
required String plan,
required String rentedDate,
required String timeLeft,
String? expiringText = "",
String? pendingPaymentText,
required bool hasPendingPayment,
}) {
// Helper widget for product item from API data
Widget _buildProductItemFromApi(Products product) {
return Stack(
children: [
// Background strip for pending payment
if (hasPendingPayment)
if (product.hasPendingPayment == true)
Container(
height: 124 + 50, // Main content height + strip height
height: 130 + 50,
padding: EdgeInsets.zero,
decoration: BoxDecoration(
color: Colors.red.withOpacity(0.6),
......@@ -580,7 +579,7 @@ class _DashboardScreenState extends State<DashboardScreen> {
),
child: Column(
children: [
SizedBox(height: 138),
SizedBox(height: 144),
// Pending payment strip
Container(
width: double.infinity,
......@@ -602,7 +601,7 @@ class _DashboardScreenState extends State<DashboardScreen> {
const SizedBox(width: 6),
Expanded(
child: Text(
pendingPaymentText ?? "Payment Pending",
product.pendingPaymentText ?? "Payment Pending",
style: TextStyle(
fontFamily: "Poppins",
color: Colors.black87,
......@@ -643,23 +642,16 @@ class _DashboardScreenState extends State<DashboardScreen> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Expiring badge (if any)
if (expiringText == "months left")
// Expiring badge
if (product.expiringText != null && product.expiringText!.isNotEmpty)
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(0xFFE9FFDD),
Color(0xFFB5FFD1),
],
),
gradient: _getGradientByColor(product.expiringInColor),
),
child: Text(
"$timeLeft months left",
product.expiringText!,
style: const TextStyle(
color: Colors.black87,
fontSize: 12,
......@@ -669,34 +661,11 @@ class _DashboardScreenState extends State<DashboardScreen> {
),
),
if (expiringText == "Expiring in")
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,
),
),
),
const SizedBox(height: 8),
// Product name and plan
Text(
productName,
product.productName ?? "Unknown Product",
style: const TextStyle(
color: Colors.black,
fontSize: 16,
......@@ -717,7 +686,7 @@ class _DashboardScreenState extends State<DashboardScreen> {
),
),
Text(
"₹$plan",
"₹${product.plan ?? "0"}",
style: const TextStyle(
fontFamily: "Poppins",
color: Color(0xFF008CDE),
......@@ -731,7 +700,7 @@ class _DashboardScreenState extends State<DashboardScreen> {
// Rented date
Text(
rentedDate,
product.rentedDate ?? "Rented date not available",
style: TextStyle(
fontFamily: "Poppins",
color: Colors.grey.shade500,
......@@ -740,9 +709,6 @@ class _DashboardScreenState extends State<DashboardScreen> {
fontSize: 12,
),
),
const SizedBox(height: 1),
],
),
),
......@@ -756,7 +722,22 @@ class _DashboardScreenState extends State<DashboardScreen> {
color: const Color(0xffF2F2F2),
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',
height: 80,
width: 80,
......@@ -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) {
showModalBottomSheet(
......@@ -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';
import 'package:gen_rentals/Screens/ProductsDetailScreen.dart';
import 'package:gen_rentals/Screens/TransactionsScreen.dart';
import '../Utility/CustomSnackbar.dart';
import '../Utility/SharedpreferencesService.dart';
import 'authScreen/LoginScreen.dart';
class SplashScreen extends StatefulWidget {
......@@ -24,11 +25,12 @@ class _SplashScreenState extends State<SplashScreen> with SingleTickerProviderSt
late Animation<Offset> _slideAnimation;
late Animation<double> _rotationAnimation;
final prefs = SharedPreferencesService.instance;
Timer? _connectivityTimer;
bool _progressCheckCompleted = false;
bool _hasInternet = true;
@override
void initState() {
super.initState();
......@@ -77,10 +79,38 @@ class _SplashScreenState extends State<SplashScreen> with SingleTickerProviderSt
// Navigate after delay
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(
context,
PageRouteBuilder(
pageBuilder: (_, __, ___) => LoginScreen(),
pageBuilder: (_, __, ___) => DashboardScreen(accId: savedAccId),
transitionsBuilder: (_, animation, __, child) {
return FadeTransition(
opacity: animation,
......@@ -90,9 +120,27 @@ class _SplashScreenState extends State<SplashScreen> with SingleTickerProviderSt
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 {
try {
......@@ -146,10 +194,6 @@ class _SplashScreenState extends State<SplashScreen> with SingleTickerProviderSt
} else {
// Dismiss the warning snackbar if internet is restored
ScaffoldMessenger.of(context).hideCurrentSnackBar();
// if (!_progressCheckCompleted) {
// _startLoginCheck();
// }
}
}
......@@ -165,20 +209,18 @@ class _SplashScreenState extends State<SplashScreen> with SingleTickerProviderSt
action: SnackBarAction(
label: "RETRY",
onPressed: () {
// Retry logic
_checkConnectivity();
},
),
);
});
}
}
@override
void dispose() {
_controller.dispose();
_connectivityTimer?.cancel();
super.dispose();
}
......
......@@ -64,27 +64,25 @@ class _TransactionsScreenState extends State<TransactionsScreen> {
child: Scaffold(
backgroundColor: AppColors.backgroundRegular,
appBar: AppBar(
automaticallyImplyLeading: false,
backgroundColor: Colors.white,
elevation: 0,
titleSpacing: 0,
title: Row(
children: [
InkResponse(
onTap: () => Navigator.pop(context),
onTap: () => Navigator.pop(context, true),
child: SvgPicture.asset(
"assets/svg/continue_left_ic.svg",
height: 25,
width: 25,
height: 25
),
),
const SizedBox(width: 12),
const Text(
const SizedBox(width: 10),
Text(
"Transactions",
style: TextStyle(
fontSize: 16,
fontFamily: "Poppins",
fontWeight: FontWeight.w500,
color: Colors.black,
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w600,
color: Colors.black87,
),
),
],
......
......@@ -5,6 +5,7 @@ import 'package:provider/provider.dart';
import '../../Notifier/RentalContactProvider .dart';
import '../../Utility/AdvancedSnackbar.dart';
import '../../Utility/CustomSnackbar.dart';
import '../../Utility/SharedpreferencesService.dart';
class LoginScreen extends StatefulWidget {
const LoginScreen({super.key});
......@@ -18,6 +19,8 @@ class _LoginScreenState extends State<LoginScreen> {
bool _isValid = false;
bool _isDirty = false;
final prefs = SharedPreferencesService.instance;
void _validatePhone(String value) {
setState(() {
_isDirty = true;
......@@ -39,8 +42,9 @@ class _LoginScreenState extends State<LoginScreen> {
message: rentalProvider.response?.errorMsg ?? "OTP sent to your registered mobile number!",
);
// Navigate to OTP screen after a short delay
Future.delayed(const Duration(milliseconds: 1500), () {
Future.delayed(const Duration(milliseconds: 500), () {
if (mounted) {
Navigator.pushReplacement(
context,
......@@ -55,7 +59,7 @@ class _LoginScreenState extends State<LoginScreen> {
CustomSnackBar.showWarning(
context: context,
title: "Login Failed",
message: rentalProvider.response?.errorMsg ??
message: rentalProvider.response?.errorMsg.toString() ??
"Mobile number not registered or invalid",
);
}
......
......@@ -5,6 +5,7 @@ import 'package:provider/provider.dart';
import '../../Notifier/RentalContactProvider .dart';
import '../../Utility/AdvancedSnackbar.dart';
import '../../Utility/CustomSnackbar.dart';
import '../../Utility/SharedpreferencesService.dart';
class OtpScreen extends StatefulWidget {
final String mob; // phone number
......@@ -19,6 +20,7 @@ class OtpScreen extends StatefulWidget {
}
class _OtpScreenState extends State<OtpScreen> {
final prefs = SharedPreferencesService.instance;
final List<TextEditingController> _controllers =
List.generate(4, (_) => TextEditingController());
final List<FocusNode> _focusNodes = List.generate(4, (_) => FocusNode());
......@@ -71,6 +73,7 @@ class _OtpScreenState extends State<OtpScreen> {
setState(() => _isVerifying = true);
try {
// Remove .toString() since enteredOtp is already a string
await rentalProvider.fetchMobileOtp(widget.mob, enteredOtp);
if (rentalProvider.otpResponse != null) {
......@@ -84,10 +87,16 @@ class _OtpScreenState extends State<OtpScreen> {
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
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (_) => const DashboardScreen()),
MaterialPageRoute(builder: (_) => DashboardScreen(accId: rentalProvider.otpResponse!.accId!,)),
);
} else {
// ❌ Invalid OTP
......@@ -100,11 +109,11 @@ class _OtpScreenState extends State<OtpScreen> {
);
}
} else {
// ❌ API Error
// ❌ API Error - Response is null
CustomSnackBar.showError(
context: context,
title: "Error",
message: rentalProvider.otpResponse?.message ?? "Failed to verify OTP. Please try again.",
message: "Failed to verify OTP. Please try again.",
);
}
} catch (e) {
......
......@@ -35,4 +35,4 @@ const ticketListUrl = "${baseUrl}ticket_list";
/// dashboard and login
const fetchMobileUrl = "${baseUrl}fetch_mobile_number";
const fetchOtpUrl = "${baseUrl}login";
const dashboardUrl = "${baseUrl}ticket_list";
\ No newline at end of file
const dashboardUrl = "${baseUrl}dashboard";
\ No newline at end of file
......@@ -534,18 +534,18 @@ class ApiCalling {
/// Fetch Dashboard
static Future<DashboardResponse?> fetchDashboardApi(
String sessionId,
String empId,
String mob,
String accId,
) async {
debugPrint("Dashboard Api called ##############");
try {
Map<String, String> data = {
"session_id": sessionId,
"emp_id": empId,
"mob": mob,
"acc_id": accId,
};
debugPrint("Account Id : $accId");
final res = await post(data, dashboardUrl, {});
debugPrint(res?.body);
if (res != null) {
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:
dependency: transitive
description:
name: fake_async
sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc"
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
url: "https://pub.dev"
source: hosted
version: "1.3.2"
version: "1.3.3"
ffi:
dependency: transitive
description:
......@@ -284,26 +284,26 @@ packages:
dependency: transitive
description:
name: leak_tracker
sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec
sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
url: "https://pub.dev"
source: hosted
version: "10.0.8"
version: "11.0.2"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
url: "https://pub.dev"
source: hosted
version: "3.0.9"
version: "3.0.10"
leak_tracker_testing:
dependency: transitive
description:
name: leak_tracker_testing
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
url: "https://pub.dev"
source: hosted
version: "3.0.1"
version: "3.0.2"
lints:
dependency: transitive
description:
......@@ -705,10 +705,10 @@ packages:
dependency: transitive
description:
name: test_api
sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd
sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00"
url: "https://pub.dev"
source: hosted
version: "0.7.4"
version: "0.7.6"
typed_data:
dependency: transitive
description:
......@@ -753,10 +753,10 @@ packages:
dependency: transitive
description:
name: vector_math
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
url: "https://pub.dev"
source: hosted
version: "2.1.4"
version: "2.2.0"
vm_service:
dependency: transitive
description:
......@@ -798,5 +798,5 @@ packages:
source: hosted
version: "6.5.0"
sdks:
dart: ">=3.7.2 <4.0.0"
dart: ">=3.8.0-0 <4.0.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