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

New Screen and api

parent 9462b0ba
...@@ -2,7 +2,8 @@ ...@@ -2,7 +2,8 @@
<application <application
android:label="Gen Rentals" android:label="Gen Rentals"
android:name="${applicationName}" android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"> android:icon="@mipmap/ic_launcher"
android:enableOnBackInvokedCallback="true"> <!-- Fixed: Removed extra quote and corrected placement -->
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:exported="true" android:exported="true"
......
org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true android.useAndroidX=true
android.enableJetifier=true android.enableJetifier=true
flutter.minSdkVersion=23
<svg width="38" height="38" viewBox="0 0 38 38" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M18.9809 37.9618C29.4637 37.9618 37.9618 29.4637 37.9618 18.9809C37.9618 8.49803 29.4637 0 18.9809 0C8.49803 0 0 8.49803 0 18.9809C0 29.4637 8.49803 37.9618 18.9809 37.9618Z" fill="url(#paint0_linear_383_1201)"/>
<path d="M18.8177 32.4369C17.2122 32.4369 15.8743 31.1325 15.8743 29.5271C15.8743 27.9216 17.2122 26.6172 18.8177 26.6172C20.4231 26.6172 21.761 27.9216 21.761 29.5271C21.761 31.1325 20.4231 32.4369 18.8177 32.4369Z" fill="white"/>
<path d="M20.7245 22.1017V24.6105H16.0086L15.2058 18.6567C19.186 18.6567 21.9954 17.7872 21.9954 15.2451C21.9954 13.6061 20.7915 12.3018 18.7178 12.3018C17.38 12.3018 15.6404 12.9037 14.102 13.9072L13.4663 9.15762C14.8712 8.18757 17.0789 7.48535 19.1525 7.48535C23.8349 7.48535 27.347 10.6628 27.347 14.8438C27.347 18.8908 24.0024 21.6671 20.7245 22.1017Z" fill="white"/>
<defs>
<linearGradient id="paint0_linear_383_1201" x1="25.4745" y1="36.8219" x2="12.4873" y2="1.13985" gradientUnits="userSpaceOnUse">
<stop offset="0.2486" stop-color="#948BF7"/>
<stop offset="0.9279" stop-color="#C7CBFF"/>
</linearGradient>
</defs>
</svg>
class DashboardResponse { class DashboardResponse {
String? error; String? error;
String? mobNum;
String? raname; String? raname;
String? balanceAmount; int? balanceAmount;
String? message; String? message;
List<Products>? products; List<Orders>? orders;
DashboardResponse( DashboardResponse(
{this.error, {this.error,
this.mobNum,
this.raname, this.raname,
this.balanceAmount, this.balanceAmount,
this.message, this.message,
this.products}); this.orders});
DashboardResponse.fromJson(Map<String, dynamic> json) { DashboardResponse.fromJson(Map<String, dynamic> json) {
error = json['error']; error = json['error'];
mobNum = json['mob_num'];
raname = json['raname']; raname = json['raname'];
balanceAmount = json['balance_amount']; balanceAmount = json['balance_amount'];
message = json['message']; message = json['message'];
if (json['products'] != null) { if (json['orders'] != null) {
products = <Products>[]; orders = <Orders>[];
json['products'].forEach((v) { json['orders'].forEach((v) {
products!.add(new Products.fromJson(v)); orders!.add(new Orders.fromJson(v));
}); });
} }
} }
...@@ -28,65 +31,62 @@ class DashboardResponse { ...@@ -28,65 +31,62 @@ 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['mob_num'] = this.mobNum;
data['raname'] = this.raname; data['raname'] = this.raname;
data['balance_amount'] = this.balanceAmount; data['balance_amount'] = this.balanceAmount;
data['message'] = this.message; data['message'] = this.message;
if (this.products != null) { if (this.orders != null) {
data['products'] = this.products!.map((v) => v.toJson()).toList(); data['orders'] = this.orders!.map((v) => v.toJson()).toList();
} }
return data; return data;
} }
} }
class Products { class Orders {
String? orderid; String? orderid;
String? productName; String? orderNum;
String? productImage; String? productImage;
String? plan;
String? rentedDate; String? rentedDate;
String? expiringInColor; String? expiringInColor;
String? expiringText; String? expiringText;
String? address;
bool? hasPendingPayment; bool? hasPendingPayment;
String? pendingPaymentText; String? pendingPaymentText;
List<String>? products;
Products( Orders(
{this.orderid, {this.orderid,
this.productName, this.orderNum,
this.productImage, this.productImage,
this.plan,
this.rentedDate, this.rentedDate,
this.expiringInColor, this.expiringInColor,
this.expiringText, this.expiringText,
this.address,
this.hasPendingPayment, this.hasPendingPayment,
this.pendingPaymentText}); this.pendingPaymentText,
this.products});
Products.fromJson(Map<String, dynamic> json) { Orders.fromJson(Map<String, dynamic> json) {
orderid = json['orderid']; orderid = json['orderid'];
productName = json['productName']; orderNum = json['order_num'];
productImage = json['productImage']; productImage = json['productImage'];
plan = json['plan'];
rentedDate = json['rentedDate']; rentedDate = json['rentedDate'];
expiringInColor = json['ExpiringInColor']; expiringInColor = json['ExpiringInColor'];
expiringText = json['expiringText']; expiringText = json['expiringText'];
address = json['address'];
hasPendingPayment = json['hasPendingPayment']; hasPendingPayment = json['hasPendingPayment'];
pendingPaymentText = json['pendingPaymentText']; pendingPaymentText = json['pendingPaymentText'];
products = json['products'].cast<String>();
} }
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['orderid'] = this.orderid; data['orderid'] = this.orderid;
data['productName'] = this.productName; data['order_num'] = this.orderNum;
data['productImage'] = this.productImage; data['productImage'] = this.productImage;
data['plan'] = this.plan;
data['rentedDate'] = this.rentedDate; data['rentedDate'] = this.rentedDate;
data['ExpiringInColor'] = this.expiringInColor; data['ExpiringInColor'] = this.expiringInColor;
data['expiringText'] = this.expiringText; data['expiringText'] = this.expiringText;
data['address'] = this.address;
data['hasPendingPayment'] = this.hasPendingPayment; data['hasPendingPayment'] = this.hasPendingPayment;
data['pendingPaymentText'] = this.pendingPaymentText; data['pendingPaymentText'] = this.pendingPaymentText;
data['products'] = this.products;
return data; return data;
} }
} }
class SubscribeOrderDetailsResponse {
String? orderid;
String? orderNum;
String? rentedDate;
String? expiringInColor;
String? expiringText;
List<Products>? products;
String? error;
String? message;
SubscribeOrderDetailsResponse(
{this.orderid,
this.orderNum,
this.rentedDate,
this.expiringInColor,
this.expiringText,
this.products,
this.error,
this.message});
SubscribeOrderDetailsResponse.fromJson(Map<String, dynamic> json) {
orderid = json['orderid'];
orderNum = json['order_num'];
rentedDate = json['rentedDate'];
expiringInColor = json['ExpiringInColor'];
expiringText = json['expiringText'];
if (json['products'] != null) {
products = <Products>[];
json['products'].forEach((v) {
products!.add(new Products.fromJson(v));
});
}
error = json['error'];
message = json['message'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['orderid'] = this.orderid;
data['order_num'] = this.orderNum;
data['rentedDate'] = this.rentedDate;
data['ExpiringInColor'] = this.expiringInColor;
data['expiringText'] = this.expiringText;
if (this.products != null) {
data['products'] = this.products!.map((v) => v.toJson()).toList();
}
data['error'] = this.error;
data['message'] = this.message;
return data;
}
}
class Products {
String? id;
String? idName;
String? prodName;
String? totalPrice;
String? per;
String? dispatchDate;
String? receivedDate;
Products(
{this.id,
this.idName,
this.prodName,
this.totalPrice,
this.per,
this.dispatchDate,
this.receivedDate});
Products.fromJson(Map<String, dynamic> json) {
id = json['id'];
idName = json['id_name'];
prodName = json['prod_name'];
totalPrice = json['total_price'];
per = json['per'];
dispatchDate = json['dispatch_date'];
receivedDate = json['received_date'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['id_name'] = this.idName;
data['prod_name'] = this.prodName;
data['total_price'] = this.totalPrice;
data['per'] = this.per;
data['dispatch_date'] = this.dispatchDate;
data['received_date'] = this.receivedDate;
return data;
}
}
class TransactionsResponse {
int? balanceAmount;
String? creditAmount;
String? debitAmount;
Map<String, List<TransactionItem>>? transactions;
String? error;
String? message;
TransactionsResponse({
this.balanceAmount,
this.creditAmount,
this.debitAmount,
this.transactions,
this.error,
this.message,
});
TransactionsResponse.fromJson(Map<String, dynamic> json) {
balanceAmount = json['balance_amount'];
creditAmount = json['credit_amount'];
debitAmount = json['debit_amount'];
error = json['error'];
message = json['message'];
if (json['transactions'] != null) {
transactions = {};
json['transactions'].forEach((key, value) {
transactions![key] = (value as List)
.map((v) => TransactionItem.fromJson(v))
.toList();
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = {};
data['balance_amount'] = balanceAmount;
data['credit_amount'] = creditAmount;
data['debit_amount'] = debitAmount;
data['error'] = error;
data['message'] = message;
if (transactions != null) {
data['transactions'] = transactions!.map((key, value) =>
MapEntry(key, value.map((v) => v.toJson()).toList()));
}
return data;
}
}
class TransactionItem {
String? paymentId;
String? amount;
String? type;
String? purpose;
String? date;
TransactionItem({
this.paymentId,
this.amount,
this.type,
this.purpose,
this.date,
});
TransactionItem.fromJson(Map<String, dynamic> json) {
paymentId = json['payment_id'];
amount = json['amount'];
type = json['type'];
purpose = json['purpose'];
date = json['date'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = {};
data['payment_id'] = paymentId;
data['amount'] = amount;
data['type'] = type;
data['purpose'] = purpose;
data['date'] = date;
return data;
}
}
class OrderListResponse {
List<Order>? order;
int? error;
String? message;
OrderListResponse({this.order, this.error, this.message});
OrderListResponse.fromJson(Map<String, dynamic> json) {
if (json['order'] != null) {
order = <Order>[];
json['order'].forEach((v) {
order!.add(new Order.fromJson(v));
});
}
error = json['error'];
message = json['message'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
if (this.order != null) {
data['order'] = this.order!.map((v) => v.toJson()).toList();
}
data['error'] = this.error;
data['message'] = this.message;
return data;
}
}
class Order {
String? orderid;
String? datetime;
String? address;
Order({this.orderid, this.datetime, this.address});
Order.fromJson(Map<String, dynamic> json) {
orderid = json['orderid'];
datetime = json['datetime'];
address = json['address'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['orderid'] = this.orderid;
data['datetime'] = this.datetime;
data['address'] = this.address;
return data;
}
}
...@@ -15,6 +15,7 @@ class DashboardProvider with ChangeNotifier { ...@@ -15,6 +15,7 @@ class DashboardProvider with ChangeNotifier {
/// Fetch Dashboard API /// Fetch Dashboard API
Future<void> fetchDashboard({ Future<void> fetchDashboard({
required String accId, required String accId,
required String sessionId,
}) async { }) async {
_isLoading = true; _isLoading = true;
_errorMessage = null; _errorMessage = null;
...@@ -22,7 +23,7 @@ class DashboardProvider with ChangeNotifier { ...@@ -22,7 +23,7 @@ class DashboardProvider with ChangeNotifier {
try { try {
final response = final response =
await ApiCalling.fetchDashboardApi(accId,); await ApiCalling.fetchDashboardApi(accId, sessionId);
if (response != null) { if (response != null) {
_dashboardData = response; _dashboardData = response;
......
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:gen_rentals/Services/api_calling.dart'; import 'package:gen_rentals/Services/api_calling.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'dart:io';
class RentalProvider extends ChangeNotifier { class RentalProvider extends ChangeNotifier {
FetchMobileResponse? _response; FetchMobileResponse? _response;
FetchMobileResponse? otpResponse; FetchMobileResponse? otpResponse;
bool _isLoading = false; bool _isLoading = false;
FetchMobileResponse? get response => _response; FetchMobileResponse? get response => _response;
bool get isLoading => _isLoading; bool get isLoading => _isLoading;
bool isOtpLoading = false; bool isOtpLoading = false;
...@@ -33,7 +36,8 @@ class RentalProvider extends ChangeNotifier { ...@@ -33,7 +36,8 @@ class RentalProvider extends ChangeNotifier {
notifyListeners(); notifyListeners();
try { try {
final result = await ApiCalling.fetchMobileOtpApi(mob, otp); final deviceDetails = await getDeviceDetails();
final result = await ApiCalling.fetchMobileOtpApi(mob, otp, deviceDetails);
otpResponse = result; otpResponse = result;
} catch (e) { } catch (e) {
debugPrint("❌ OTP API Error: $e"); debugPrint("❌ OTP API Error: $e");
...@@ -44,6 +48,41 @@ class RentalProvider extends ChangeNotifier { ...@@ -44,6 +48,41 @@ class RentalProvider extends ChangeNotifier {
} }
} }
Future<Map<String, String>> getDeviceDetails() async {
final deviceInfo = DeviceInfoPlugin();
if (Platform.isAndroid) {
final androidInfo = await deviceInfo.androidInfo;
return {
"versionName": androidInfo.version.release ?? "",
"versionCode": androidInfo.version.codename ?? "",
"osVersion": androidInfo.version.release ?? "",
"sdkVersion": androidInfo.version.sdkInt.toString(),
"device": androidInfo.device ?? "",
"model": androidInfo.model ?? "",
"product": androidInfo.product ?? "",
"manufacturer": androidInfo.manufacturer ?? "",
"brand": androidInfo.brand ?? "",
"user": "",
"display": androidInfo.display ?? "",
"hardware": androidInfo.hardware ?? "",
"board": androidInfo.board ?? "",
"host": androidInfo.host ?? "",
"serial": androidInfo.serialNumber ?? "unknown",
"id": androidInfo.id ?? "",
"bootloader": androidInfo.bootloader ?? "",
"cpuAbi1": androidInfo.supportedAbis.isNotEmpty ? androidInfo.supportedAbis[0] : "",
"cpuAbi2": androidInfo.supportedAbis.length > 1 ? androidInfo.supportedAbis[1] : "",
"fingerprint": androidInfo.fingerprint ?? "",
};
}
return {};
}
} }
...@@ -52,6 +91,7 @@ class FetchMobileResponse { ...@@ -52,6 +91,7 @@ class FetchMobileResponse {
String? errorMsg; String? errorMsg;
String? accId; String? accId;
String? message; String? message;
String? sessionId;
FetchMobileResponse({this.error, this.errorMsg}); FetchMobileResponse({this.error, this.errorMsg});
...@@ -60,6 +100,7 @@ class FetchMobileResponse { ...@@ -60,6 +100,7 @@ class FetchMobileResponse {
errorMsg = json['error_msg']; errorMsg = json['error_msg'];
accId = json['acc_id']; accId = json['acc_id'];
message = json['message']; message = json['message'];
sessionId = json['session_id'];
} }
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
...@@ -68,7 +109,9 @@ class FetchMobileResponse { ...@@ -68,7 +109,9 @@ class FetchMobileResponse {
data['error_msg'] = this.errorMsg; data['error_msg'] = this.errorMsg;
data['acc_id'] = this.accId; data['acc_id'] = this.accId;
data['message'] = this.message; data['message'] = this.message;
data['session_id'] = this.sessionId;
return data; return data;
} }
} }
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:gen_rentals/Services/api_calling.dart';
import 'dart:convert';
import '../Models/SubscribeOrderDetailsResponse.dart';
class SubscribeOrderDetailsProvider with ChangeNotifier {
SubscribeOrderDetailsResponse? _orderDetails;
bool _isLoading = false;
String _errorMessage = '';
// Getters
SubscribeOrderDetailsResponse? get orderDetails => _orderDetails;
bool get isLoading => _isLoading;
String get errorMessage => _errorMessage;
/// Fetch Subscribe Order Details
Future<void> fetchSubscribeOrderDetails(
String sessionId,
String orderId,
String accId,
) async {
_isLoading = true;
_errorMessage = '';
notifyListeners();
try {
final response = await ApiCalling.fetchSubsOrderDetailApi(
sessionId,
orderId,
accId,
);
if (response != null) {
// Check if error is "0" which means success in your API
if (response.error == "0") {
_orderDetails = response;
_errorMessage = '';
}
// Handle other error cases
else if (response.error != null && response.error!.isNotEmpty && response.error != "0") {
_errorMessage = response.message ?? 'An error occurred';
_orderDetails = null;
}
// Handle message-based errors
else if (response.message != null &&
response.message!.isNotEmpty &&
!response.message!.toLowerCase().contains('success')) {
_errorMessage = response.message!;
_orderDetails = null;
}
// Default success case
else {
_orderDetails = response;
_errorMessage = '';
}
} else {
_errorMessage = 'Failed to load order details';
_orderDetails = null;
}
} catch (e) {
_errorMessage = 'An error occurred: $e';
_orderDetails = null;
debugPrint("❌ Provider Error (fetchSubscribeOrderDetails): $e");
} finally {
_isLoading = false;
notifyListeners();
}
}
// ... rest of your provider code
}
\ No newline at end of file
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:gen_rentals/Services/api_calling.dart';
import '../Models/TransactionsResponse.dart';
class TransactionsProvider with ChangeNotifier {
bool _isLoading = false;
TransactionsResponse? _transactionsResponse;
bool get isLoading => _isLoading;
TransactionsResponse? get transactionsResponse => _transactionsResponse;
/// For UI convenience
Map<String, List<TransactionItem>> get transactionsByMonth =>
_transactionsResponse?.transactions ?? {};
/// Fetch Rental Transactions API
Future<void> fetchRentalTransactions(String sessionId, String accId) async {
_isLoading = true;
notifyListeners();
try {
final response =
await ApiCalling.fetchRentalsTransactionsApi(sessionId, accId);
if (response != null && response.error == "0") {
_transactionsResponse = response;
log("✅ Transactions fetched successfully: ${response.transactions?.length ?? 0} months");
} else {
log("⚠️ Error in API response: ${response?.message}");
_transactionsResponse = null;
}
} catch (e) {
log("❌ Exception in fetchRentalTransactions: $e");
_transactionsResponse = null;
} finally {
_isLoading = false;
notifyListeners();
}
}
/// Optional: Clear data
void clearTransactions() {
_transactionsResponse = null;
notifyListeners();
}
}
...@@ -11,7 +11,8 @@ import 'authScreen/LoginScreen.dart'; ...@@ -11,7 +11,8 @@ import 'authScreen/LoginScreen.dart';
class DashboardScreen extends StatefulWidget { class DashboardScreen extends StatefulWidget {
final String accId; final String accId;
const DashboardScreen({super.key, required this.accId}); final String sessionId;
const DashboardScreen({super.key, required this.accId, required this.sessionId});
@override @override
State<DashboardScreen> createState() => _DashboardScreenState(); State<DashboardScreen> createState() => _DashboardScreenState();
...@@ -25,7 +26,7 @@ class _DashboardScreenState extends State<DashboardScreen> { ...@@ -25,7 +26,7 @@ class _DashboardScreenState extends State<DashboardScreen> {
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
// Only fetch dashboard if accId is valid // Only fetch dashboard if accId is valid
if (widget.accId.isNotEmpty && widget.accId != "null") { if (widget.accId.isNotEmpty && widget.accId != "null") {
Provider.of<DashboardProvider>(context, listen: false).fetchDashboard(accId: widget.accId); Provider.of<DashboardProvider>(context, listen: false).fetchDashboard(accId: widget.accId, sessionId: widget.sessionId);
} else { } else {
// If accId is invalid, navigate back to login // If accId is invalid, navigate back to login
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
...@@ -182,9 +183,11 @@ class _DashboardScreenState extends State<DashboardScreen> { ...@@ -182,9 +183,11 @@ class _DashboardScreenState extends State<DashboardScreen> {
// Main content section // Main content section
Container( Container(
padding: const EdgeInsets.symmetric(horizontal: 20), padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column( child: Column(
children: [ children: [
// Balance Amount Card // Balance Amount Card
Container( Container(
width: double.infinity, width: double.infinity,
...@@ -222,7 +225,7 @@ class _DashboardScreenState extends State<DashboardScreen> { ...@@ -222,7 +225,7 @@ class _DashboardScreenState extends State<DashboardScreen> {
children: [ children: [
// Use provider data for balance amount - FIXED NULL CHECK // Use provider data for balance amount - FIXED NULL CHECK
Text( Text(
dashboardData?.balanceAmount ?? "0", dashboardData?.balanceAmount.toString() ?? "0",
style: const TextStyle( style: const TextStyle(
fontFamily: "Poppins", fontFamily: "Poppins",
color: Colors.black, color: Colors.black,
...@@ -260,6 +263,70 @@ class _DashboardScreenState extends State<DashboardScreen> { ...@@ -260,6 +263,70 @@ class _DashboardScreenState extends State<DashboardScreen> {
const SizedBox(height: 20), const SizedBox(height: 20),
const SizedBox(height: 20),
// Subscribed Orders
Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 2, vertical: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Section Title
Text(
"Subscribed Orders",
style: TextStyle(
fontFamily: "Poppins",
color: Colors.grey.shade800,
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 16),
// Show loading or products list
if (dashboardProvider.isLoading && dashboardData == null)
const Center(
child: CircularProgressIndicator(),
)
else if (dashboardData?.orders == null || dashboardData!.orders!.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!.orders!.map((product) {
return Column(
children: [
InkResponse(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => ProductsDetailScreen(
sessionId: widget.sessionId,
accId: widget.accId,
orderId: product.orderid.toString(),
)),
);
},
child: _buildProductItemFromApi(product),
),
const SizedBox(height: 16),
],
);
}).toList(),
),
],
),
),
// Feature cards grid // Feature cards grid
Container( Container(
width: double.infinity, width: double.infinity,
...@@ -279,7 +346,7 @@ class _DashboardScreenState extends State<DashboardScreen> { ...@@ -279,7 +346,7 @@ class _DashboardScreenState extends State<DashboardScreen> {
); );
}, },
child: Container( child: Container(
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10), padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 8),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: Colors.white,
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
...@@ -305,7 +372,7 @@ class _DashboardScreenState extends State<DashboardScreen> { ...@@ -305,7 +372,7 @@ class _DashboardScreenState extends State<DashboardScreen> {
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
), ),
), ),
const SizedBox(height: 8), const SizedBox(height: 6),
Text( Text(
"Submit your enquiry for requirement", "Submit your enquiry for requirement",
style: TextStyle( style: TextStyle(
...@@ -417,7 +484,10 @@ class _DashboardScreenState extends State<DashboardScreen> { ...@@ -417,7 +484,10 @@ class _DashboardScreenState extends State<DashboardScreen> {
onTap: () { onTap: () {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute(builder: (context) => TransactionsScreen()), MaterialPageRoute(builder: (context) => TransactionsScreen(
sessionId: widget.sessionId,
accId: widget.accId,
)),
); );
}, },
child: Container( child: Container(
...@@ -487,64 +557,7 @@ class _DashboardScreenState extends State<DashboardScreen> { ...@@ -487,64 +557,7 @@ class _DashboardScreenState extends State<DashboardScreen> {
], ],
), ),
), ),
const SizedBox(height: 20), SizedBox(height: 10,),
// Subscribed Products
Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 2, vertical: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Section Title
Text(
"Subscribed Products",
style: TextStyle(
fontFamily: "Poppins",
color: Colors.grey.shade800,
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 16),
// 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: [
InkResponse(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => ProductsDetailScreen()),
);
},
child: _buildProductItemFromApi(product),
),
const SizedBox(height: 16),
],
);
}).toList(),
),
],
),
)
], ],
), ),
), ),
...@@ -561,224 +574,267 @@ class _DashboardScreenState extends State<DashboardScreen> { ...@@ -561,224 +574,267 @@ class _DashboardScreenState extends State<DashboardScreen> {
} }
// Helper widget for product item from API data // Helper widget for product item from API data
Widget _buildProductItemFromApi(Products product) { Widget _buildProductItemFromApi(Orders product) {
return Stack( final bool hasPending = product.hasPendingPayment == true;
children: [ final productList = product.products ?? [];
// Background strip for pending payment
if (product.hasPendingPayment == true) return Container(
Container( margin: const EdgeInsets.symmetric(vertical: 6),
height: 130 + 50,
padding: EdgeInsets.zero,
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.red.withOpacity(0.6),
borderRadius: BorderRadius.circular(15), borderRadius: BorderRadius.circular(15),
border: Border.all( boxShadow: [
color: Colors.grey.shade200, BoxShadow(
width: 1, color: Colors.grey.withOpacity(0.15),
blurRadius: 6,
offset: const Offset(0, 2),
), ),
],
), ),
child: Column( child: Stack(
children: [ children: [
SizedBox(height: 144), // ===== Red Strip (Behind Card) =====
// Pending payment strip if (hasPending)
Container( Positioned.fill(
width: double.infinity, top: null,
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 8), child: Align(
decoration: BoxDecoration( alignment: Alignment.bottomCenter,
color: Colors.white38, child: Container(
borderRadius: const BorderRadius.only( height: 45,
bottomLeft: Radius.circular(10), decoration: const BoxDecoration(
bottomRight: Radius.circular(10), color: Color(0xFFFFE2E0),
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(15),
bottomRight: Radius.circular(15),
), ),
), ),
child: Row( child: Row(
children: [ children: [
Icon( const SizedBox(width: 12),
Icons.info_outline, const Icon(Icons.info_outline, color: Colors.red, size: 18),
color: Colors.red,
size: 18,
),
const SizedBox(width: 6), const SizedBox(width: 6),
Expanded( Expanded(
child: Text( child: Text(
product.pendingPaymentText ?? "Payment Pending", product.pendingPaymentText ??
style: TextStyle( "Payment Pending. Please Pay before incurring fines.",
style: const TextStyle(
fontFamily: "Poppins", fontFamily: "Poppins",
color: Colors.black87, color: Colors.red,
fontSize: 12, fontSize: 12,
fontStyle: FontStyle.normal,
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
), ),
), ),
), ),
const SizedBox(width: 12),
], ],
), ),
), ),
],
), ),
), ),
// Main content card // ===== Main White Card =====
Container( Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: Colors.white,
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(15),
border: Border.all(
color: Colors.grey.shade200,
width: 1,
),
), ),
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
// Main content /// Header Row (image, order id, date, badge)
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Expanded( Row(
flex: 6,
child: Padding(
padding: const EdgeInsets.all(10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
// Expiring badge
if (product.expiringText != null && product.expiringText!.isNotEmpty)
Container( Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6), padding: const EdgeInsets.all(14),
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8), color: const Color(0xffF2F2F2),
gradient: _getGradientByColor(product.expiringInColor), borderRadius: BorderRadius.circular(16),
),
child: Text(
product.expiringText!,
style: const TextStyle(
color: Colors.black87,
fontSize: 12,
fontStyle: FontStyle.normal,
fontWeight: FontWeight.w400,
), ),
child: Image.network(
product.productImage ?? "",
height: 40,
width: 40,
fit: BoxFit.contain,
errorBuilder: (context, error, stack) =>
Image.asset('assets/images/gene_png.png',
height: 40, width: 40),
), ),
), ),
const SizedBox(width: 8),
const SizedBox(height: 8), Column(
crossAxisAlignment: CrossAxisAlignment.start,
// Product name and plan children: [
Text( Text(
product.productName ?? "Unknown Product", "#${product.orderid ?? "0"}",
style: const TextStyle( style: const TextStyle(
color: Colors.black, fontFamily: "Poppins",
color: Color(0xFF008CDE),
fontSize: 16, fontSize: 16,
fontStyle: FontStyle.normal, fontWeight: FontWeight.w500,
fontWeight: FontWeight.w400, height: 1.2,
), ),
), ),
const SizedBox(height: 4),
Text( Text(
"Plan", product.rentedDate ?? "Rented date not available",
style: TextStyle( style: TextStyle(
fontFamily: "Poppins", fontFamily: "Poppins",
color: Colors.grey.shade600, color: Colors.grey.shade600,
fontWeight: FontWeight.w400,
fontStyle: FontStyle.normal,
fontSize: 12, fontSize: 12,
height: 1,
), ),
), ),
Text( ],
"₹${product.plan ?? "0"}", ),
],
),
// ✅ Gradient expiry badge
if (product.expiringText != null &&
product.expiringText!.isNotEmpty)
Container(
padding: const EdgeInsets.symmetric(
horizontal: 10, vertical: 6),
decoration: BoxDecoration(
gradient: _getGradientByColor(product.expiringInColor),
borderRadius: BorderRadius.circular(8),
),
child: Text(
product.expiringText!,
style: const TextStyle( style: const TextStyle(
fontFamily: "Poppins", color: Colors.black87,
color: Color(0xFF008CDE), fontSize: 12,
fontSize: 18,
fontStyle: FontStyle.normal,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
height: 1.2
), ),
), ),
const SizedBox(height: 3), ),
],
),
// Rented date const SizedBox(height: 6),
Text( const Divider(),
product.rentedDate ?? "Rented date not available",
style: TextStyle( /// ===== Product List (with +3 More on same line) =====
fontFamily: "Poppins", Builder(
color: Colors.grey.shade500, builder: (context) {
fontStyle: FontStyle.normal, final visibleItems = productList.take(2).toList();
final remaining = productList.length - visibleItems.length;
return Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// Left side → Product list (bulleted)
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
for (int i = 0; i < visibleItems.length; i++)
Padding(
padding: const EdgeInsets.only(bottom: 4),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(width: 8),
const Text(
"• ",
style: TextStyle(color: Colors.black, fontSize: 16),
),
Expanded(
child: Text(
visibleItems[i],
style: const TextStyle(
color: Colors.black,
fontSize: 16,
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
fontSize: 12, ),
), ),
), ),
], ],
), ),
), ),
],
),
), ),
Expanded( // Right side → +x More (vertically centered)
flex: 3, if (remaining > 0)
child: Container( Padding(
padding: const EdgeInsets.all(14), padding: const EdgeInsets.only(left: 8, right: 4),
decoration: BoxDecoration( child: Align(
color: const Color(0xffF2F2F2), alignment: Alignment.center,
borderRadius: BorderRadius.circular(16), child: Text(
"+$remaining More",
style: const TextStyle(
fontFamily: "Poppins",
color: Color(0xFF008CDE),
fontSize: 14,
fontWeight: FontWeight.w500,
), ),
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,
fit: BoxFit.contain,
), ),
), ),
), ),
], ],
);
},
), ),
], ],
), ),
), ),
], ],
),
); );
} }
// Helper method to get gradient based on color from API // Gradient helper
LinearGradient _getGradientByColor(String? color) { LinearGradient _getGradientByColor(String? color) {
switch (color) { switch (color) {
case "red": case "Red":
return const LinearGradient( return const LinearGradient(
colors: [Color(0xFFFFE0E0), Color(0xFFFFC0C0)],
begin: Alignment.topLeft, begin: Alignment.topLeft,
end: Alignment.bottomRight, end: Alignment.bottomRight,
colors: [
Color(0xFFFFDDDD),
Color(0xFFFFB5B5),
],
); );
case "green": case "Green":
default: default:
return const LinearGradient( return const LinearGradient(
colors: [Color(0xFFE9FFDD), Color(0xFFB5FFD1)],
begin: Alignment.topLeft, begin: Alignment.topLeft,
end: Alignment.bottomRight, end: Alignment.bottomRight,
colors: [
Color(0xFFE9FFDD),
Color(0xFFB5FFD1),
],
); );
} }
} }
// 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(
context: context, context: context,
......
import 'dart:convert';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:gen_rentals/Screens/BillDetailListScreen.dart'; import 'package:gen_rentals/Screens/BillDetailListScreen.dart';
import 'package:gen_rentals/Utility/Reusablewidgets.dart'; import 'package:provider/provider.dart';
import '../Models/SubscribeOrderDetailsResponse.dart';
import '../Notifier/SubscribeOrderDetailsProvider.dart';
import '../Utility/AppColors.dart'; import '../Utility/AppColors.dart';
import 'HelpScreens/CreateTicketScreen.dart';
class ProductsDetailScreen extends StatelessWidget { class ProductsDetailScreen extends StatefulWidget {
const ProductsDetailScreen({super.key}); final String sessionId;
final String orderId;
final String accId;
const ProductsDetailScreen({
super.key,
required this.sessionId,
required this.orderId,
required this.accId,
});
@override
State<ProductsDetailScreen> createState() => _ProductsDetailScreenState();
}
class _ProductsDetailScreenState extends State<ProductsDetailScreen> {
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),
},
];
@override
void initState() {
super.initState();
// Fetch order details when screen loads
WidgetsBinding.instance.addPostFrameCallback((_) {
_loadOrderDetails();
});
}
void _loadOrderDetails() {
final provider = Provider.of<SubscribeOrderDetailsProvider>(context, listen: false);
provider.fetchSubscribeOrderDetails(
widget.sessionId,
widget.orderId,
widget.accId,
);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
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;
return Consumer<SubscribeOrderDetailsProvider>(
builder: (context, provider, child) {
return SafeArea( return SafeArea(
top: false, top: false,
child: Scaffold( child: Scaffold(
body: Container( appBar: AppBar(
color: const Color(0xFFF3F3F3), automaticallyImplyLeading: false,
height: screenHeight, backgroundColor: Colors.white,
child: SingleChildScrollView( title: Row(
child: Column(
children: [ children: [
// Top background image section InkResponse(
Stack( onTap: () => Navigator.pop(context, true),
children: [ child: SvgPicture.asset(
// Background image "assets/svg/continue_left_ic.svg",
Container( height: 25,
width: double.infinity,
decoration: BoxDecoration(
gradient: const LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.white54,
Color(0xFFF3F3F3),
],
), ),
), ),
child: Image.asset( const SizedBox(width: 10),
'assets/images/sky_blue_bg.jpg', const Text(
width: double.infinity, "Bill List",
height: 400, style: TextStyle(
fit: BoxFit.cover, fontSize: 16,
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w600,
color: Colors.black87,
), ),
), ),
// Content overlay
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Header with profile
Container(
width: double.infinity,
height: 450,
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Color(0x22FFFFFF),
Color(0x33FFFFFF),
Color(0x88FFFFFF),
Color(0xFFF3F3F3),
Color(0xFFF3F3F3),
], ],
), ),
), ),
body: _buildBody(provider, screenHeight, bottomPadding),
),
);
},
);
}
Widget _buildBody(SubscribeOrderDetailsProvider provider, double screenHeight, double bottomPadding) {
if (provider.isLoading) {
return const Center(
child: CircularProgressIndicator(),
);
}
if (provider.errorMessage.isNotEmpty) {
return Center(
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
const SizedBox(height: 50), Text(
// Instead of SizedBox(width: 280) - consider using Spacer() provider.errorMessage,
Row( style: const TextStyle(
mainAxisAlignment: MainAxisAlignment.spaceBetween, fontSize: 16,
children: [ fontFamily: "Poppins",
SvgPicture.asset( color: Colors.red,
"assets/svg/continue_left_ic.svg",
height: 50,
width: 50,
),
// Removed SizedBox(width: 280)
Container(
width: 50,
height: 50,
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.19),
shape: BoxShape.circle,
border: Border.all(
color: Colors.white.withOpacity(0.5),
width: 2,
),
), ),
child: const Icon(
Icons.person,
color: Colors.white,
size: 30,
), ),
const SizedBox(height: 16),
ElevatedButton(
onPressed: _loadOrderDetails,
child: const Text('Retry'),
), ),
], ],
), ),
Column( );
crossAxisAlignment: CrossAxisAlignment.center, }
mainAxisAlignment: MainAxisAlignment.start,
children: [
Image.asset(
'assets/images/gen2_img.png',
height: 250,
width: 250,
),
const Text(
"Genesis 85kVA",
style: TextStyle(
fontFamily: "Poppins",
color: Colors.black,
fontSize: 30,
fontWeight: FontWeight.w500,
),
),
Container( if (provider.orderDetails == null) {
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6), return const Center(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
gradient: const LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
Color(0xFFE9FFDD),
Color(0xFFB5FFD1),
],
),
),
child: Text( child: Text(
"2 months left", 'No order details found',
style: const TextStyle(
color: Colors.black87,
fontSize: 12,
fontStyle: FontStyle.normal,
fontWeight: FontWeight.w400,
),
),
),
SizedBox(height: 4,),
const Text(
"Rented on 7th July, 2025",
style: TextStyle( style: TextStyle(
fontSize: 16,
fontFamily: "Poppins", fontFamily: "Poppins",
color: Colors.grey, color: Colors.grey,
fontSize: 16,
fontWeight: FontWeight.w400,
),
),
],
),
],
), ),
), ),
);
}
const SizedBox(height: 10), final order = provider.orderDetails!;
// Main content section return Container(
Container( color: const Color(0xFFF3F3F3),
padding: const EdgeInsets.symmetric(horizontal: 20), height: screenHeight,
child: SingleChildScrollView(
child: Column( child: Column(
children: [ children: [
// Balance Amount Card // Order header (not in card)
Container( Container(
width: double.infinity, width: double.infinity,
padding: const EdgeInsets.all(20), margin: const EdgeInsets.all(16),
decoration: BoxDecoration( padding: const EdgeInsets.all(16),
color: Colors.white,
borderRadius: BorderRadius.circular(16),
),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.center,
children: [
InkResponse(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => BillDetailListScreen()),
);
},
child: Row(
children: [ children: [
const Text(
"Balance Amount",
style: TextStyle(
fontFamily: "Poppins",
color: AppColors.warningText,
fontSize: 14,
fontWeight: FontWeight.w400,
),
),
const SizedBox(width: 4),
SvgPicture.asset(
"assets/svg/continue_ic.svg",
color: AppColors.warningText,
height: 18,
width: 18,
),
],
),
),
const SizedBox(height: 8),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
const Text( const Text(
"₹24,800", "# Order ID ",
style: TextStyle(
fontFamily: "Poppins",
color: AppColors.cardAmountText,
fontSize: 32,
fontWeight: FontWeight.w500,
),
),
InkResponse(
onTap: () => showPaymentBottomSheet(context),
child: Text(
"Pay Now",
style: TextStyle(
fontFamily: "Poppins",
color: AppColors.amountText,
fontSize: 14,
fontWeight: FontWeight.w500,
),
),
),
],
),
const SizedBox(height: 2),
Text(
"Last Paid on 12th Sep, 2025",
style: TextStyle( style: TextStyle(
fontSize: 24,
fontFamily: "Poppins", fontFamily: "Poppins",
color: AppColors.subtitleText,
fontSize: 12,
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
// fontStyle: FontStyle.italic, color: Colors.black87,
),
), ),
SvgPicture.asset(
"assets/svg/line_ic.svg",
color: AppColors.subtitleText,
height: 18,
width: 18,
), ),
// After spacer
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text( Text(
"Plan", "#${order.orderNum ?? order.orderid ?? 'N/A'}",
style: TextStyle( style: TextStyle(
fontSize: 24,
fontFamily: "Poppins", fontFamily: "Poppins",
color: AppColors.subtitleText,
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
fontStyle: FontStyle.normal, color: AppColors.amountText,
fontSize: 12,
),
),
Text(
"₹3600",
style: const TextStyle(
fontFamily: "Poppins",
color: Color(0xFF008CDE),
fontSize: 18,
fontStyle: FontStyle.normal,
fontWeight: FontWeight.w500,
), ),
), ),
], ],
), ),
const SizedBox(height: 8),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Deposit",
style: TextStyle(
fontFamily: "Poppins",
color: AppColors.subtitleText,
fontWeight: FontWeight.w400,
fontStyle: FontStyle.normal,
fontSize: 12,
),
),
Text( Text(
"₹5483", order.rentedDate ?? 'Date not available',
style: const TextStyle( style: const TextStyle(
fontSize: 14,
fontFamily: "Poppins", fontFamily: "Poppins",
color: AppColors.cardAmountText,
fontSize: 18,
fontStyle: FontStyle.normal,
fontWeight: FontWeight.w500,
),
),
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Tenure",
style: TextStyle(
fontFamily: "Poppins",
color: AppColors.subtitleText,
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
fontStyle: FontStyle.normal, color: Colors.grey,
fontSize: 12,
), ),
), ),
Text( const SizedBox(height: 14),
"6 Months", Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: _getExpiringColor(order.expiringInColor),
borderRadius: BorderRadius.circular(16),
),
child: Text(
order.expiringText ?? 'Expiring info not available',
style: const TextStyle( style: const TextStyle(
fontSize: 12,
fontFamily: "Poppins", fontFamily: "Poppins",
color: AppColors.cardAmountText,
fontSize: 18,
fontStyle: FontStyle.normal,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
color: Colors.white,
),
), ),
), ),
],
)
],
)
], ],
), ),
), ),
const SizedBox(height: 18), // Products section
if (order.products != null && order.products!.isNotEmpty)
Container( Container(
width: double.infinity, width: double.infinity,
color: Colors.transparent, margin: const EdgeInsets.symmetric(horizontal: 2),
padding: const EdgeInsets.all(16),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const SizedBox(height: 2), const Text(
SectionHeading(title: "Generator Overview", padding: EdgeInsets.symmetric(vertical: 4),), "Products",
Text(
"A diesel generator converts fuel into electricity using an engine to drive an alternator. They provide backup power for homes and businesses and serve as primary sources in remote areas.",
style: TextStyle( style: TextStyle(
fontSize: 18,
fontFamily: "Poppins", fontFamily: "Poppins",
color: AppColors.subtitleText, fontWeight: FontWeight.w600,
fontWeight: FontWeight.w400, color: Colors.black87,
fontStyle: FontStyle.normal,
fontSize: 12,
), ),
), ),
const SizedBox(height: 16),
], // Product list using ListView.builder
) ListView.separated(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: order.products!.length,
separatorBuilder: (context, index) => const SizedBox(height: 16),
itemBuilder: (context, index) {
return _buildProductItem(order.products![index]);
},
), ),
const SizedBox(height: 20),
// Feature cards grid const SizedBox(height: 16),
Container( // Divider
width: double.infinity, const Divider(
color: Colors.transparent, color: Color(0xFFE5E5E5),
child: Column( thickness: 1,
children: [
// First row of feature cards
Row(
children: [
// Any Requirements Card
Expanded(
child: _buildFeatureCard(
title: "85 kVA",
description: "PRIME POWER",
icon: "assets/svg/power_ic.svg",
color: Colors.orange,
),
), ),
const SizedBox(width: 16), const SizedBox(height: 16),
// Subscribed Products Card
Expanded( // Help section
child: _buildFeatureCard( InkResponse(
title: "1500 RPM", onTap: () => _showReasonBottomSheet(),
description: "ENGINE SPEED", child: Row(
icon: "assets/svg/rpm_ic.svg", children: [
color: Colors.blue, Text(
"Need help with this order?",
style: TextStyle(
fontSize: 14,
fontFamily: "Poppins",
fontWeight: FontWeight.w500,
color: Colors.grey,
), ),
), ),
], ],
), ),
),
const SizedBox(height: 16), const SizedBox(height: 16),
// Second row of feature cards // View Bill button
Row( SizedBox(
children: [ width: double.infinity,
// Have Complaints Card child: ElevatedButton(
Expanded( onPressed: () {
child: _buildFeatureCard( Navigator.push(
title: "75 dB", context,
description: "LOW NOISE LEVEL", MaterialPageRoute(builder: (context) => BillDetailListScreen())
icon: "assets/svg/noise_ic.svg", );
color: Colors.green, // Handle view bill 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),
), ),
const SizedBox(width: 16), elevation: 0,
// Know Your Payments Card
Expanded(
child: _buildFeatureCard(
title: "24 Hr",
description: "LONGER RUNTIME",
icon: "assets/svg/clock_ic.svg",
color: Colors.purple,
), ),
child: const Text(
"View Bill",
style: TextStyle(
fontSize: 16,
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w600,
), ),
],
), ),
const SizedBox(height: 20),
],
), ),
), ),
], ],
), ),
)
else
const Padding(
padding: EdgeInsets.all(16.0),
child: Text(
'No products found',
style: TextStyle(
fontSize: 16,
fontFamily: "Poppins",
color: Colors.grey,
), ),
],
), ),
],
), ),
SizedBox(height: bottomPadding + 20),
], ],
), ),
), ),
),
),
); );
} }
Widget _buildProductItem(Products product) {
Widget _buildFeatureCard({
required String title,
required String description,
required String icon,
required Color color,
}) {
return Container( return Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), padding: const EdgeInsets.all(16),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.grey.shade50, color: Colors.white,
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(18),
border: Border.all( // border: Border.all(
color: Colors.grey.shade200, // color: const Color(0xFFE5E5E5),
width: 1, // width: 1,
),
// boxShadow: [
// BoxShadow(
// color: Colors.black.withOpacity(0.05),
// blurRadius: 8,
// offset: const Offset(0, 2),
// ), // ),
// ],
), ),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
// Icon and title row // Product ID and Name
Container(
width: 45,
height: 45,
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
),
child: Center( // Add this Center widget
child: SvgPicture.asset(
icon,
height: 25,
width: 25,
// Remove fit: BoxFit.none
),
),
),
const SizedBox(height: 4),
Text( Text(
title, product.idName ?? product.id ?? 'N/A',
style: TextStyle( style: const TextStyle(
color: AppColors.nearDarkText,
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.w600, fontFamily: "Poppins",
fontWeight: FontWeight.w500,
color: Colors.grey,
), ),
), ),
const SizedBox(height: 4), const SizedBox(height: 4),
// Description
Text( Text(
description, product.prodName ?? 'Product name not available',
style: TextStyle( style: const TextStyle(
color: AppColors.subtitleText, fontSize: 16,
fontFamily: "Poppins", fontFamily: "Poppins",
fontWeight: FontWeight.w400, fontWeight: FontWeight.w600,
fontSize: 12, color: Colors.black87,
),
),
],
),
);
}
void showPaymentBottomSheet(BuildContext context) {
showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (BuildContext context) {
return SafeArea(
bottom: true,
child: Container(
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(24),
topRight: Radius.circular(24),
),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Header - Drag handle
Center(
child: Container(
width: 40,
height: 4,
decoration: BoxDecoration(
color: Colors.grey.shade300,
borderRadius: BorderRadius.circular(2),
),
), ),
), ),
const SizedBox(height: 20), const SizedBox(height: 12),
// Pay Amount Section // Table-like layout for dates and price
Row( Table(
mainAxisAlignment: MainAxisAlignment.spaceBetween, columnWidths: const {
0: FlexColumnWidth(2),
1: FlexColumnWidth(1),
},
children: [ children: [
const Column( TableRow(
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( TableCell(
"Pay", child: Text(
style: TextStyle( product.dispatchDate != null ?
fontSize: 16, "Dispatched On ${product.dispatchDate!}" :
"Dispatch date not available",
style: const TextStyle(
fontSize: 12,
fontFamily: "Poppins",
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
color: Color(0xFF777777), color: Colors.grey,
), ),
), ),
SizedBox(height: 4), ),
Text( const TableCell(
"₹4218", child: Align(
alignment: Alignment.centerRight,
child: Text(
"Plan",
style: TextStyle( style: TextStyle(
fontSize: 24, fontSize: 12,
fontWeight: FontWeight.w600, fontFamily: "Poppins",
color: Colors.black, fontWeight: FontWeight.w500,
color: Colors.black87,
),
),
), ),
), ),
], ],
), ),
TableRow(
// Rent Amount Section
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [ children: [
const Text( TableCell(
"Rent Amount", child: Text(
style: TextStyle( product.receivedDate != null ?
fontSize: 14, "Received On ${product.receivedDate!}" :
"Receive date not available",
style: const TextStyle(
fontSize: 12,
fontFamily: "Poppins",
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
color: Color(0xFF777777), color: Colors.grey,
), ),
), ),
const SizedBox(height: 4), ),
GestureDetector( TableCell(
onTap: () { child: Align(
// Handle view bill details alignment: Alignment.centerRight,
},
child: Text( child: Text(
"View Bill Details", product.totalPrice != null ?
style: TextStyle( "${product.totalPrice!}/${product.per ?? 'mo'}" :
'Price not available',
style: const TextStyle(
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.w500, fontFamily: "Poppins",
color: Color(0xFF008CDE), fontWeight: FontWeight.w600,
color: Colors.black87,
),
), ),
), ),
), ),
...@@ -603,58 +440,141 @@ class ProductsDetailScreen extends StatelessWidget { ...@@ -603,58 +440,141 @@ class ProductsDetailScreen extends StatelessWidget {
), ),
], ],
), ),
],
),
);
}
const SizedBox(height: 10), Color _getExpiringColor(String? colorString) {
const Divider(height: 1, color: Color(0xFFEEEEEE)), if (colorString == null || colorString.isEmpty) {
const SizedBox(height: 18), return const Color(0xFFFFEBEB); // Default color
}
// Continue Payment Button try {
SizedBox( // Assuming colorString is in format like "FFFF5757"
width: double.infinity, return Color(int.parse('FF$colorString', radix: 16));
child: ElevatedButton( } catch (e) {
onPressed: () { return const Color(0xFFFFEFEF); // Default color on error
// Handle continue payment }
}, }
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF008CDE), void _showReasonBottomSheet() {
foregroundColor: Colors.white, // Your existing bottom sheet implementation
disabledBackgroundColor: const Color(0xFF266E99), showModalBottomSheet(
shape: RoundedRectangleBorder( context: context,
borderRadius: BorderRadius.circular(30), backgroundColor: Colors.white,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20),
topRight: Radius.circular(20),
), ),
padding: const EdgeInsets.symmetric(vertical: 16),
), ),
child: Padding( builder: (context) {
padding: const EdgeInsets.symmetric(horizontal: 22), return Container(
child: Row( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 14),
mainAxisAlignment: MainAxisAlignment.spaceBetween, child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const Text( const Text(
"Continue Payment", "Select Your Reason",
style: TextStyle( style: TextStyle(
color: Color(0xFFFFFFFF), fontSize: 18,
fontSize: 16, fontFamily: "Poppins",
fontWeight: FontWeight.w600,
color: Colors.black87,
), ),
), ),
SvgPicture.asset( const SizedBox(height: 24),
"assets/svg/continue_ic.svg",
color: Color(0xFFFFFFFF), GridView.builder(
height: 25, shrinkWrap: true,
width: 25, 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,
);
},
), ),
const SizedBox(height: 24),
], ],
), ),
);
},
);
}
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),
}, ],
),
),
); );
} }
} }
\ No newline at end of file
...@@ -104,13 +104,14 @@ class _SplashScreenState extends State<SplashScreen> with SingleTickerProviderSt ...@@ -104,13 +104,14 @@ class _SplashScreenState extends State<SplashScreen> with SingleTickerProviderSt
Future<void> _navigateToDashboard() async { Future<void> _navigateToDashboard() async {
final String? savedAccId = await prefs.getString("accId"); final String? savedAccId = await prefs.getString("accId");
final String? savedSessionId = await prefs.getString("session_id");
// Check if accId is not null and not empty // Check if accId is not null and not empty
if (savedAccId != null && savedAccId.isNotEmpty) { if (savedAccId != null && savedAccId.isNotEmpty) {
Navigator.pushReplacement( Navigator.pushReplacement(
context, context,
PageRouteBuilder( PageRouteBuilder(
pageBuilder: (_, __, ___) => DashboardScreen(accId: savedAccId), pageBuilder: (_, __, ___) => DashboardScreen(accId: savedAccId, sessionId: savedSessionId.toString(),),
transitionsBuilder: (_, animation, __, child) { transitionsBuilder: (_, animation, __, child) {
return FadeTransition( return FadeTransition(
opacity: animation, opacity: animation,
......
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:provider/provider.dart';
import '../Notifier/TransactionsProvider.dart';
import '../Utility/AppColors.dart'; import '../Utility/AppColors.dart';
class TransactionsScreen extends StatefulWidget { class TransactionsScreen extends StatefulWidget {
const TransactionsScreen({super.key}); final String sessionId;
final String accId;
const TransactionsScreen({
super.key,
required this.sessionId,
required this.accId,
});
@override @override
State<TransactionsScreen> createState() => _TransactionsScreenState(); State<TransactionsScreen> createState() => _TransactionsScreenState();
} }
class _TransactionsScreenState extends State<TransactionsScreen> { class _TransactionsScreenState extends State<TransactionsScreen> {
// Dummy data for transactions @override
final List<Map<String, dynamic>> transactions = [ void initState() {
{ super.initState();
"type": "debit", Future.microtask(() {
"title": "Payment for June Rent", Provider.of<TransactionsProvider>(context, listen: false)
"date": "25 Dec 2025", .fetchRentalTransactions(widget.sessionId, widget.accId);
"amount": "₹5,390.00", });
"ref": "₹1,12,300" }
},
{
"type": "debit",
"title": "Payment for June Rent",
"date": "25 Dec 2025",
"amount": "₹2,512.00",
"ref": "₹26,300"
},
{
"type": "credit",
"title": "Refund of Deposit Amount",
"date": "25 Dec 2025",
"amount": "₹19,790.00",
"ref": "₹1,26,300"
},
{
"type": "debit",
"title": "Payment for June Rent",
"date": "25 Dec 2025",
"amount": "₹5,390.00",
"ref": "₹1,12,300"
},
{
"type": "debit",
"title": "Payment for June Rent",
"date": "25 Dec 2025",
"amount": "₹2,512.00",
"ref": "₹26,300"
},
{
"type": "credit",
"title": "Refund of Deposit Amount",
"date": "25 Dec 2025",
"amount": "₹19,790.00",
"ref": "₹1,26,300"
},
];
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final provider = Provider.of<TransactionsProvider>(context);
final data = provider.transactionsResponse;
return SafeArea( return SafeArea(
top: false, top: false,
child: Scaffold( child: Scaffold(
...@@ -72,11 +46,11 @@ class _TransactionsScreenState extends State<TransactionsScreen> { ...@@ -72,11 +46,11 @@ class _TransactionsScreenState extends State<TransactionsScreen> {
onTap: () => Navigator.pop(context, true), 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,
), ),
), ),
const SizedBox(width: 10), const SizedBox(width: 10),
Text( const Text(
"Transactions", "Transactions",
style: TextStyle( style: TextStyle(
fontSize: 16, fontSize: 16,
...@@ -88,208 +62,341 @@ class _TransactionsScreenState extends State<TransactionsScreen> { ...@@ -88,208 +62,341 @@ class _TransactionsScreenState extends State<TransactionsScreen> {
], ],
), ),
), ),
// Main content
body: SingleChildScrollView( /// ✅ Main Content
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), body: provider.isLoading
? const Center(child: CircularProgressIndicator())
: data == null
? const Center(child: Text("No transactions available"))
: SingleChildScrollView(
padding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildBalanceCard(data),
const SizedBox(height: 16),
/// 🗓️ Monthly Grouped Transactions
...data.transactions!.entries.map((entry) {
final month = entry.key;
final items = entry.value;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 12),
Text(
month,
style: const TextStyle(
fontSize: 15,
fontFamily: "Poppins",
fontWeight: FontWeight.w600,
color: Colors.black,
),
),
const SizedBox(height: 8),
...items.map((txn) => _buildTransactionItem(
type: txn.type ?? "",
title: txn.purpose ?? "-",
date: txn.date ?? "",
amount: "₹${txn.amount ?? "0.00"}",
)),
],
);
}),
],
),
),
),
);
}
/// 💰 Balance Card (Top Section)
Widget _buildBalanceCard(data) {
final balance = data.balanceAmount ?? "0";
final credit = data.creditAmount ?? "0";
final debit = data.debitAmount ?? "0";
return Stack(
children: [ children: [
SizedBox(height: 4,),
// Balance Card
Container( Container(
width: double.infinity, width: double.infinity,
padding: const EdgeInsets.symmetric(vertical: 24, horizontal: 16), padding: const EdgeInsets.symmetric(vertical: 24, horizontal: 18),
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20), gradient: LinearGradient(
gradient: const LinearGradient( begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [ colors: [
Color(0xFFE9FFEF), Color(0xFFF3F3F3),
Color(0xFFD6FFDF), Color(0xFFB5FFD1),
], ],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
), ),
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.03),
blurRadius: 8,
offset: const Offset(0, 2),
)
],
), ),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( /// Header
"₹12,596", Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
"Balance Amount",
style: TextStyle( style: TextStyle(
fontFamily: "Poppins", fontFamily: "Poppins",
fontSize: 13,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
fontSize: 34, color: Colors.redAccent,
color: AppColors.cardAmountText,
), ),
), ),
const SizedBox(height: 4), TextButton(
Text( onPressed: () {},
"Balance Amount", child: const Text(
"Pay Now",
style: TextStyle( style: TextStyle(
fontFamily: "Poppins", fontFamily: "Poppins",
fontWeight: FontWeight.w400, fontWeight: FontWeight.w500,
fontSize: 14, color: Color(0xFF007AFF),
color: AppColors.cardAmountText,
), ),
), ),
const SizedBox(height: 10), ),
Container( ],
height: 1, ),
color: const Color(0xFF777777).withOpacity(0.3),
Text(
"₹$balance",
style: const TextStyle(
fontFamily: "Poppins",
fontWeight: FontWeight.w600,
fontSize: 30,
color: Colors.black,
),
),
const SizedBox(height: 4),
const Text(
"*Make sure to pay before you incur any fines.",
style: TextStyle(
fontFamily: "Poppins",
fontSize: 12,
color: Colors.grey,
),
), ),
const SizedBox(height: 14), const SizedBox(height: 14),
// Credited / Debited info /// Credit / Debit Row
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [ children: [
_buildAmountSummary( // ✅ Bill Paid
icon: "assets/svg/cross_down_arrow.svg", Row(
color: Colors.white, children: [
amount: "₹26,300", Container(
label: "Credited Amount", height: 46,
), width: 46,
_buildAmountSummary( decoration: BoxDecoration(
icon: "assets/svg/cross_up_arrow.svg",
color: Colors.white, color: Colors.white,
amount: "₹26,300", borderRadius: BorderRadius.circular(22),
label: "Debited Amount",
), ),
], child: Center(
child: SvgPicture.asset(
"assets/svg/cross_down_arrow.svg",
height: 18,
width: 18,
fit: BoxFit.contain, // Ensures the SVG scales within bounds
), ),
],
), ),
), ),
const SizedBox(height: 16), const SizedBox(width: 10),
// Transaction list
Column( Column(
children: transactions.map((t) { crossAxisAlignment: CrossAxisAlignment.start,
return _buildTransactionItem( children: [
type: t["type"], Text(
title: t["title"], "₹$credit",
date: t["date"], style: const TextStyle(
amount: t["amount"], fontFamily: "Poppins",
ref: t["ref"], fontWeight: FontWeight.w500,
); fontSize: 13,
}).toList(), color: Colors.black,
), ),
],
), ),
Text(
"Bill Paid",
style: TextStyle(
fontFamily: "Poppins",
fontSize: 11,
color: Colors.black54,
), ),
), ),
); ],
} ),
],
),
Widget _buildAmountSummary({ const SizedBox(width: 10),
required String icon,
required Color color, // ❌ Bill Pending
required String amount, Row(
required String label,
}) {
return Row(
children: [ children: [
// Icon and title row
Container( Container(
width: 50, height: 46,
height: 50, width: 46,
decoration: BoxDecoration( decoration: BoxDecoration(
color: color.withOpacity(0.9), color: Colors.white,
borderRadius: BorderRadius.circular(30), borderRadius: BorderRadius.circular(22),
), ),
child: Center( // Add this Center widget child: Center(
child: SvgPicture.asset( child: SvgPicture.asset(
icon, "assets/svg/cross_up_arrow.svg",
height: 20, height: 18,
width: 20, width: 18,
// Remove fit: BoxFit.none fit: BoxFit.contain, // Ensures the SVG scales within bounds
), ),
), ),
), ),
const SizedBox(width: 8), const SizedBox(width: 10),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
amount, "₹$debit",
style: const TextStyle(
fontFamily: "Poppins",
fontWeight: FontWeight.w500,
fontSize: 13,
color: Colors.black,
),
),
Text(
"Bill Pending",
style: TextStyle( style: TextStyle(
fontFamily: "Poppins", fontFamily: "Poppins",
fontWeight: FontWeight.w400, fontSize: 11,
color: Colors.black54,
),
)
],
),
],
),
],
),
],
),
),
/// White overlay card (unchanged)
Container(
width: double.infinity,
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
const Text(
"Balance Amount",
style: TextStyle(
fontFamily: "Poppins",
color: Color(0xFFF00000),
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.w400,
),
),
const SizedBox(width: 4),
SvgPicture.asset(
"assets/svg/continue_ic.svg",
color: const Color(0xFFF00000),
height: 18,
width: 18,
),
],
),
const SizedBox(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"₹$balance",
style: const TextStyle(
fontFamily: "Poppins",
color: Colors.black, color: Colors.black,
fontSize: 32,
fontWeight: FontWeight.w500,
), ),
), ),
InkResponse(
child: const Text(
"Pay Now",
style: TextStyle(
fontFamily: "Poppins",
color: Color(0xFF008CDE),
fontSize: 14,
fontWeight: FontWeight.w500,
),
),
),
],
),
const SizedBox(height: 12),
Text( Text(
label, "*Make sure to pay before you incur any fines.",
style: TextStyle( style: TextStyle(
fontFamily: "Poppins", fontFamily: "Poppins",
fontWeight: FontWeight.w400, color: Colors.grey.shade500,
fontSize: 12, fontSize: 12,
color: Colors.grey.shade600, fontWeight: FontWeight.w400,
), ),
), ),
], ],
), ),
),
], ],
); );
} }
/// 📋 Transaction Item
Widget _buildTransactionItem({ Widget _buildTransactionItem({
required String type, required String type,
required String title, required String title,
required String date, required String date,
required String amount, required String amount,
required String ref,
}) { }) {
final isCredit = type == "credit"; final isCredit = type.toLowerCase() == "credit";
return Container( return Container(
margin: const EdgeInsets.only(bottom: 12), margin: const EdgeInsets.only(bottom: 10),
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 14), padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 14),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: Colors.white,
borderRadius: BorderRadius.circular(14), borderRadius: BorderRadius.circular(14),
// border: Border.all(color: Colors.grey.shade200, width: 1),
// boxShadow: [
// BoxShadow(
// color: Colors.black.withOpacity(0.02),
// blurRadius: 5,
// offset: const Offset(0, 1),
// ),
// ],
), ),
child: Row( child: Row(
children: [ children: [
// Icon and title row CircleAvatar(
Container( radius: 18,
width: 50, backgroundColor:
height: 50, isCredit ? Colors.green.shade50 : Colors.red.shade50,
decoration: BoxDecoration(
color: isCredit ? Colors.green.withOpacity(0.09) : Colors.red.withOpacity(0.09),
borderRadius: BorderRadius.circular(30),
),
child: Center( // Add this Center widget
child: SvgPicture.asset( child: SvgPicture.asset(
isCredit ? "assets/svg/cross_down_arrow.svg" : "assets/svg/cross_up_arrow.svg", isCredit
height: 20, ? "assets/svg/cross_down_arrow.svg"
width: 20, : "assets/svg/cross_up_arrow.svg",
// Remove fit: BoxFit.none height: 18,
),
), ),
), ),
// Container(
// padding: const EdgeInsets.all(8),
// decoration: BoxDecoration(
// color: isCredit
// ? const Color(0xFFE8F9EE)
// : const Color(0xFFFFEBEB),
// shape: BoxShape.circle,
// ),
// child: Icon(
// isCredit ? Icons.arrow_downward_rounded : Icons.arrow_upward_rounded,
// color: isCredit ? Colors.green : Colors.red,
// size: 20,
// ),
// ),
const SizedBox(width: 12), const SizedBox(width: 12),
Expanded( Expanded(
child: Column( child: Column(
...@@ -300,7 +407,6 @@ class _TransactionsScreenState extends State<TransactionsScreen> { ...@@ -300,7 +407,6 @@ class _TransactionsScreenState extends State<TransactionsScreen> {
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: const TextStyle( style: const TextStyle(
fontFamily: "Poppins", fontFamily: "Poppins",
fontWeight: FontWeight.w400,
fontSize: 14, fontSize: 14,
color: Colors.black, color: Colors.black,
), ),
...@@ -308,52 +414,24 @@ class _TransactionsScreenState extends State<TransactionsScreen> { ...@@ -308,52 +414,24 @@ class _TransactionsScreenState extends State<TransactionsScreen> {
const SizedBox(height: 4), const SizedBox(height: 4),
Text( Text(
date, date,
style: TextStyle( style: const TextStyle(
fontFamily: "Poppins", fontFamily: "Poppins",
fontWeight: FontWeight.w400,
fontSize: 12, fontSize: 12,
color: Colors.grey.shade600, color: Colors.grey,
), ),
), ),
], ],
), ),
), ),
const SizedBox(width: 12), const SizedBox(width: 12),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text( Text(
amount, amount,
style: TextStyle( style: const TextStyle(
fontFamily: "Poppins", fontFamily: "Poppins",
fontWeight: FontWeight.w400,
fontSize: 14, fontSize: 14,
color: Colors.black, color: Colors.black,
), ),
), ),
const SizedBox(height: 4),
Row(
children: [
Text(
ref,
style: TextStyle(
fontFamily: "Poppins",
fontWeight: FontWeight.w400,
fontSize: 12,
color: Colors.grey.shade600,
),
),
const SizedBox(width: 4),//
SvgPicture.asset(
"assets/svg/rupee_coin_ic.svg",
height: 14,
width: 14,
// Remove fit: BoxFit.none
),
],
),
],
),
], ],
), ),
); );
......
...@@ -91,12 +91,16 @@ class _OtpScreenState extends State<OtpScreen> { ...@@ -91,12 +91,16 @@ class _OtpScreenState extends State<OtpScreen> {
// Added null check for accId // Added null check for accId
if (rentalProvider.otpResponse?.accId != null) { if (rentalProvider.otpResponse?.accId != null) {
await prefs.saveString("accId", rentalProvider.otpResponse!.accId!); await prefs.saveString("accId", rentalProvider.otpResponse!.accId!);
await prefs.saveString("session_id", rentalProvider.otpResponse!.sessionId!);
} }
// Navigate to dashboard // Navigate to dashboard
Navigator.pushReplacement( Navigator.pushReplacement(
context, context,
MaterialPageRoute(builder: (_) => DashboardScreen(accId: rentalProvider.otpResponse!.accId!,)), MaterialPageRoute(builder: (_) => DashboardScreen(
accId: rentalProvider.otpResponse!.accId!,
sessionId: rentalProvider.otpResponse!.sessionId!,)
),
); );
} else { } else {
// ❌ Invalid OTP // ❌ Invalid OTP
......
/// base Url of api /// base Url of api
const baseUrl = "https://erp.gengroup.in/ci/app/Inventory/"; const baseUrl = "https://erp.gengroup.in/ci/app/Rental/Rental_Home/";
const baseUrl2 = "https://erp.gengroup.in/ci/app/Rental/";
/// tokens url /// tokens url
const addFcmTokenUrl = "${baseUrl}add_fcm_token"; const addFcmTokenUrl = "${baseUrl}add_fcm_token";
/// payments and bills /// payments and bills
const addPaymentUrl = "${baseUrl}add_payment"; const addPaymentUrl = "${baseUrl}add_payment";
const rentalPaymentDetailsUrl = "${baseUrl}rental_payment_details"; const transactionsUrl = "${baseUrl}transactions";
const balanceUrl = "${baseUrl}balance"; const balanceUrl = "${baseUrl}balance";
const billDetailsUrl = "${baseUrl}bill_details"; const billDetailsUrl = "${baseUrl}bill_details";
const billListUrl = "${baseUrl}bill_list"; const billListUrl = "${baseUrl}bill_list";
...@@ -21,7 +22,7 @@ const getRentalAccInfoUrl = "${baseUrl}get_rental_acc_info"; ...@@ -21,7 +22,7 @@ const getRentalAccInfoUrl = "${baseUrl}get_rental_acc_info";
const orderDetailsBillUrl = "${baseUrl}order_details_bill"; const orderDetailsBillUrl = "${baseUrl}order_details_bill";
const orderDetailsMainUrl = "${baseUrl}order_details_main"; const orderDetailsMainUrl = "${baseUrl}order_details_main";
const orderDetailsProductUrl = "${baseUrl}order_details_product"; const orderDetailsProductUrl = "${baseUrl}order_details_product";
const orderListUrl = "${baseUrl}order_list"; const subsOrderDetails = "${baseUrl}subs_order_details";
const tagOrderUrl = "${baseUrl}tag_order"; const tagOrderUrl = "${baseUrl}tag_order";
/// tickets /// tickets
...@@ -33,6 +34,6 @@ const ticketListUrl = "${baseUrl}ticket_list"; ...@@ -33,6 +34,6 @@ const ticketListUrl = "${baseUrl}ticket_list";
/// dashboard and login /// dashboard and login
const fetchMobileUrl = "${baseUrl}fetch_mobile_number"; const fetchMobileUrl = "${baseUrl2}Rental_Auth/fetch_mobile_number";
const fetchOtpUrl = "${baseUrl}login"; const fetchOtpUrl = "${baseUrl2}Rental_Auth/login";
const dashboardUrl = "${baseUrl}dashboard"; const dashboardUrl = "${baseUrl2}Rental_Home/dashboard";
\ No newline at end of file \ No newline at end of file
...@@ -7,11 +7,12 @@ import 'package:gen_rentals/Models/billProductResponse.dart'; ...@@ -7,11 +7,12 @@ import 'package:gen_rentals/Models/billProductResponse.dart';
import 'package:gen_rentals/Models/orderDetailsBillResponse.dart'; import 'package:gen_rentals/Models/orderDetailsBillResponse.dart';
import 'package:gen_rentals/Models/orderDetailsMainResponse.dart'; import 'package:gen_rentals/Models/orderDetailsMainResponse.dart';
import 'package:gen_rentals/Models/orderDetailsProductResponse.dart'; import 'package:gen_rentals/Models/orderDetailsProductResponse.dart';
import 'package:gen_rentals/Models/orderListResponse.dart'; import 'package:gen_rentals/Models/SubscribeOrderDetailsResponse.dart';
import 'package:gen_rentals/Models/ticketListResponse.dart'; import 'package:gen_rentals/Models/ticketListResponse.dart';
import 'package:gen_rentals/Notifier/billDetailsResponse.dart'; import 'package:gen_rentals/Notifier/billDetailsResponse.dart';
import '../Models/DashboardResponse.dart'; import '../Models/DashboardResponse.dart';
import '../Models/RentalPaymentDetailsResponse.dart'; import '../Models/RentalPaymentDetailsResponse.dart';
import '../Models/TransactionsResponse.dart';
import '../Models/rentalAccountResponse.dart'; import '../Models/rentalAccountResponse.dart';
import '../Models/rentalContactResponse.dart'; import '../Models/rentalContactResponse.dart';
import '../Notifier/RentalContactProvider .dart'; import '../Notifier/RentalContactProvider .dart';
...@@ -47,13 +48,15 @@ class ApiCalling { ...@@ -47,13 +48,15 @@ class ApiCalling {
static Future<FetchMobileResponse?> fetchMobileOtpApi( static Future<FetchMobileResponse?> fetchMobileOtpApi(
String mob, String mob,
String otp String otp,
Map<String, String> deviceDetails
) async { ) async {
debugPrint("############################### Api calling "); debugPrint("############################### Api calling ");
try { try {
Map<String, String> data = { Map<String, String> data = {
"mob": mob, "mob": mob,
"otp": otp, "otp": otp,
"device_details": deviceDetails.toString(),
}; };
final res = await post(data, fetchOtpUrl, {}); final res = await post(data, fetchOtpUrl, {});
...@@ -204,23 +207,24 @@ class ApiCalling { ...@@ -204,23 +207,24 @@ class ApiCalling {
} }
} }
/// Fetch Order List /// Fetch Subscribe Order Details
static Future<OrderListResponse?> fetchOrderListApi( static Future<SubscribeOrderDetailsResponse?> fetchSubsOrderDetailApi(
String sessionId, String sessionId,
String empId, String orderId,
String accId, String accId,
) async { ) async {
try { try {
Map<String, String> data = { Map<String, String> data = {
"session_id": sessionId, "session_id": sessionId,
"emp_id": empId, "order_id": orderId,
"acc_id": accId, "acc_id": accId,
}; };
final res = await post(data, orderListUrl, {}); final res = await post(data, subsOrderDetails, {});
debugPrint("Subscribe order details Response: ${res?.body}");
if (res != null) { if (res != null) {
return OrderListResponse.fromJson(jsonDecode(res.body)); return SubscribeOrderDetailsResponse.fromJson(jsonDecode(res.body));
} else { } else {
debugPrint("Null Response"); debugPrint("Null Response");
return null; return null;
...@@ -286,23 +290,21 @@ class ApiCalling { ...@@ -286,23 +290,21 @@ class ApiCalling {
} }
} }
/// Fetch Rental Payment Details /// Fetch Rental Transaction
static Future<RentalPaymentDetailsResponse?> fetchRentalPaymentDetailsApi( static Future<TransactionsResponse?> fetchRentalsTransactionsApi(
String sessionId, String sessionId,
String empId, String accId,
String billId,
) async { ) async {
try { try {
Map<String, String> data = { Map<String, String> data = {
"session_id": sessionId, "session_id": sessionId,
"emp_id": empId, "acc_id": accId,
"bill_id": billId,
}; };
final res = await post(data, rentalPaymentDetailsUrl, {}); final res = await post(data, transactionsUrl, {});
if (res != null) { if (res != null) {
return RentalPaymentDetailsResponse.fromJson(jsonDecode(res.body)); return TransactionsResponse.fromJson(jsonDecode(res.body));
} else { } else {
debugPrint("Null Response"); debugPrint("Null Response");
return null; return null;
...@@ -535,11 +537,13 @@ class ApiCalling { ...@@ -535,11 +537,13 @@ class ApiCalling {
/// Fetch Dashboard /// Fetch Dashboard
static Future<DashboardResponse?> fetchDashboardApi( static Future<DashboardResponse?> fetchDashboardApi(
String accId, String accId,
String sessionId,
) async { ) async {
debugPrint("Dashboard Api called ##############"); debugPrint("Dashboard Api called ##############");
try { try {
Map<String, String> data = { Map<String, String> data = {
"acc_id": accId, "acc_id": accId,
"session_id": sessionId,
}; };
debugPrint("Account Id : $accId"); debugPrint("Account Id : $accId");
......
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:gen_rentals/Notifier/DashboardProvider.dart'; import 'package:gen_rentals/Notifier/DashboardProvider.dart';
import 'package:gen_rentals/Notifier/TransactionsProvider.dart';
import 'package:gen_rentals/Screens/SplashScreen.dart'; import 'package:gen_rentals/Screens/SplashScreen.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'Notifier/RentalContactProvider .dart'; import 'Notifier/RentalContactProvider .dart';
import 'Notifier/SubscribeOrderDetailsProvider.dart';
import 'Notifier/theme_provider.dart'; import 'Notifier/theme_provider.dart';
import 'Screens/authScreen/LoginScreen.dart'; import 'Screens/authScreen/LoginScreen.dart';
...@@ -28,6 +30,8 @@ class MyApp extends StatelessWidget { ...@@ -28,6 +30,8 @@ class MyApp extends StatelessWidget {
ChangeNotifierProvider<DashboardProvider>(create: (_) => DashboardProvider()), ChangeNotifierProvider<DashboardProvider>(create: (_) => DashboardProvider()),
ChangeNotifierProvider<RentalProvider>(create: (_) => RentalProvider(),), ChangeNotifierProvider<RentalProvider>(create: (_) => RentalProvider(),),
ChangeNotifierProvider<ThemeProvider>(create: (_) => ThemeProvider(),), ChangeNotifierProvider<ThemeProvider>(create: (_) => ThemeProvider(),),
ChangeNotifierProvider(create: (_) => SubscribeOrderDetailsProvider()),
ChangeNotifierProvider(create: (_) => TransactionsProvider()),
], ],
child: Consumer<ThemeProvider>( child: Consumer<ThemeProvider>(
builder: (context, themeProvider, child) { builder: (context, themeProvider, child) {
......
...@@ -129,6 +129,22 @@ packages: ...@@ -129,6 +129,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.11" version: "0.7.11"
device_info_plus:
dependency: "direct dev"
description:
name: device_info_plus
sha256: a7fd703482b391a87d60b6061d04dfdeab07826b96f9abd8f5ed98068acc0074
url: "https://pub.dev"
source: hosted
version: "10.1.2"
device_info_plus_platform_interface:
dependency: transitive
description:
name: device_info_plus_platform_interface
sha256: e1ea89119e34903dca74b883d0dd78eb762814f97fb6c76f35e9ff74d261a18f
url: "https://pub.dev"
source: hosted
version: "7.0.3"
fake_async: fake_async:
dependency: transitive dependency: transitive
description: description:
...@@ -781,6 +797,14 @@ packages: ...@@ -781,6 +797,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.13.0" version: "5.13.0"
win32_registry:
dependency: transitive
description:
name: win32_registry
sha256: "21ec76dfc731550fd3e2ce7a33a9ea90b828fdf19a5c3bcf556fa992cfa99852"
url: "https://pub.dev"
source: hosted
version: "1.1.5"
xdg_directories: xdg_directories:
dependency: transitive dependency: transitive
description: description:
......
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