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"
...@@ -17,9 +18,9 @@ ...@@ -17,9 +18,9 @@
while the Flutter UI initializes. After that, this theme continues while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. --> to determine the Window background behind the Flutter UI. -->
<meta-data <meta-data
android:name="io.flutter.embedding.android.NormalTheme" android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme" android:resource="@style/NormalTheme"
/> />
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/> <category android:name="android.intent.category.LAUNCHER"/>
...@@ -42,4 +43,4 @@ ...@@ -42,4 +43,4 @@
<data android:mimeType="text/plain"/> <data android:mimeType="text/plain"/>
</intent> </intent>
</queries> </queries>
</manifest> </manifest>
\ No newline at end of file
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,
...@@ -273,13 +340,13 @@ class _DashboardScreenState extends State<DashboardScreen> { ...@@ -273,13 +340,13 @@ class _DashboardScreenState extends State<DashboardScreen> {
child: InkResponse( child: InkResponse(
onTap: () { onTap: () {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute(builder: (context) => EnquiryScreen() MaterialPageRoute(builder: (context) => EnquiryScreen()
) )
); );
}, },
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, decoration: BoxDecoration(
padding: EdgeInsets.zero, borderRadius: BorderRadius.circular(15),
decoration: BoxDecoration( boxShadow: [
color: Colors.red.withOpacity(0.6), BoxShadow(
borderRadius: BorderRadius.circular(15), color: Colors.grey.withOpacity(0.15),
border: Border.all( blurRadius: 6,
color: Colors.grey.shade200, offset: const Offset(0, 2),
width: 1, ),
), ],
), ),
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(15),
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: Colors.grey.shade200,
width: 1,
), ),
), padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
child: Column( child: Column(
children: [ crossAxisAlignment: CrossAxisAlignment.start,
// Main content children: [
Row( /// Header Row (image, order id, date, badge)
mainAxisAlignment: MainAxisAlignment.spaceBetween, Row(
children: [ mainAxisAlignment: MainAxisAlignment.spaceBetween,
Expanded( children: [
flex: 6, Row(
child: Padding( children: [
padding: const EdgeInsets.all(10), Container(
child: Column( padding: const EdgeInsets.all(14),
crossAxisAlignment: CrossAxisAlignment.start, decoration: BoxDecoration(
children: [ color: const Color(0xffF2F2F2),
// Expiring badge borderRadius: BorderRadius.circular(16),
if (product.expiringText != null && product.expiringText!.isNotEmpty)
Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
gradient: _getGradientByColor(product.expiringInColor),
),
child: Text(
product.expiringText!,
style: const TextStyle(
color: Colors.black87,
fontSize: 12,
fontStyle: FontStyle.normal,
fontWeight: FontWeight.w400,
),
),
),
const SizedBox(height: 8),
// Product name and plan
Text(
product.productName ?? "Unknown Product",
style: const TextStyle(
color: Colors.black,
fontSize: 16,
fontStyle: FontStyle.normal,
fontWeight: FontWeight.w400,
),
), ),
const SizedBox(height: 4), child: Image.network(
Text( product.productImage ?? "",
"Plan", height: 40,
style: TextStyle( width: 40,
fontFamily: "Poppins", fit: BoxFit.contain,
color: Colors.grey.shade600, errorBuilder: (context, error, stack) =>
fontWeight: FontWeight.w400, Image.asset('assets/images/gene_png.png',
fontStyle: FontStyle.normal, height: 40, width: 40),
fontSize: 12,
height: 1,
),
), ),
Text( ),
"₹${product.plan ?? "0"}", const SizedBox(width: 8),
style: const TextStyle( Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"#${product.orderid ?? "0"}",
style: const TextStyle(
fontFamily: "Poppins", fontFamily: "Poppins",
color: Color(0xFF008CDE), color: Color(0xFF008CDE),
fontSize: 18, fontSize: 16,
fontStyle: FontStyle.normal,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
height: 1.2 height: 1.2,
),
),
Text(
product.rentedDate ?? "Rented date not available",
style: TextStyle(
fontFamily: "Poppins",
color: Colors.grey.shade600,
fontSize: 12,
),
), ),
],
),
],
),
// ✅ 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(
color: Colors.black87,
fontSize: 12,
fontWeight: FontWeight.w500,
), ),
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();
fontWeight: FontWeight.w400, final remaining = productList.length - visibleItems.length;
fontSize: 12,
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,
),
),
),
],
),
),
],
),
),
// Right side → +x More (vertically centered)
if (remaining > 0)
Padding(
padding: const EdgeInsets.only(left: 8, right: 4),
child: Align(
alignment: Alignment.center,
child: Text(
"+$remaining More",
style: const TextStyle(
fontFamily: "Poppins",
color: Color(0xFF008CDE),
fontSize: 14,
fontWeight: FontWeight.w500,
),
),
), ),
), ),
], ],
), );
), },
), ),
Expanded( ],
flex: 3, ),
child: Container(
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
color: const Color(0xffF2F2F2),
borderRadius: BorderRadius.circular(16),
),
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 StatefulWidget {
final String sessionId;
final String orderId;
final String accId;
class ProductsDetailScreen extends StatelessWidget { const ProductsDetailScreen({
const ProductsDetailScreen({super.key}); super.key,
required this.sessionId,
required this.orderId,
required this.accId,
});
@override @override
Widget build(BuildContext context) { State<ProductsDetailScreen> createState() => _ProductsDetailScreenState();
double screenWidth = MediaQuery.of(context).size.width; }
double screenHeight = MediaQuery.of(context).size.height;
double bottomPadding = MediaQuery.of(context).padding.bottom;
return SafeArea( class _ProductsDetailScreenState extends State<ProductsDetailScreen> {
top: false,
child: Scaffold(
body: Container(
color: const Color(0xFFF3F3F3),
height: screenHeight,
child: SingleChildScrollView(
child: Column(
children: [
// Top background image section
Stack(
children: [
// Background image
Container(
width: double.infinity,
decoration: BoxDecoration(
gradient: const LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.white54,
Color(0xFFF3F3F3),
],
),
),
child: Image.asset(
'assets/images/sky_blue_bg.jpg',
width: double.infinity,
height: 400,
fit: BoxFit.cover,
),
),
// Content overlay final List<Map<String, dynamic>> createNewTickets = [
Column( {
crossAxisAlignment: CrossAxisAlignment.start, 'title': 'Payment Issues',
children: [ 'description': 'Get help with payment related problems',
// Header with profile 'icon': "assets/svg/rupee_coin_ic.svg",
Container( 'color': Color(0xFFFFEFBE),
width: double.infinity, },
height: 450, {
decoration: const BoxDecoration( 'title': 'Bill Related Issues',
gradient: LinearGradient( 'description': 'Resolve bill and invoice matters',
begin: Alignment.topCenter, 'icon': "assets/svg/know_pay.svg",
end: Alignment.bottomCenter, 'color': Color(0xFFCEF9FF),
colors: [ },
Color(0x22FFFFFF), {
Color(0x33FFFFFF), 'title': 'Other Issues',
Color(0x88FFFFFF), 'description': 'Any other support you need',
Color(0xFFF3F3F3), 'icon': "assets/svg/help_ic.svg",
Color(0xFFF3F3F3), 'color': Color(0xFFE4E5FF),
], },
), ];
),
child: Column(
children: [
const SizedBox(height: 50),
// Instead of SizedBox(width: 280) - consider using Spacer()
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SvgPicture.asset(
"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,
),
),
],
),
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(
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),
],
),
),
child: Text(
"2 months left",
style: const TextStyle(
color: Colors.black87,
fontSize: 12,
fontStyle: FontStyle.normal,
fontWeight: FontWeight.w400,
),
),
),
SizedBox(height: 4,), @override
const Text( void initState() {
"Rented on 7th July, 2025", super.initState();
style: TextStyle( // Fetch order details when screen loads
fontFamily: "Poppins", WidgetsBinding.instance.addPostFrameCallback((_) {
color: Colors.grey, _loadOrderDetails();
fontSize: 16, });
fontWeight: FontWeight.w400, }
),
),
],
),
],
),
),
const SizedBox(height: 10), void _loadOrderDetails() {
final provider = Provider.of<SubscribeOrderDetailsProvider>(context, listen: false);
provider.fetchSubscribeOrderDetails(
widget.sessionId,
widget.orderId,
widget.accId,
);
}
// Main content section @override
Container( Widget build(BuildContext context) {
padding: const EdgeInsets.symmetric(horizontal: 20), double screenHeight = MediaQuery.of(context).size.height;
child: Column( double bottomPadding = MediaQuery.of(context).padding.bottom;
children: [
// Balance Amount Card
Container(
width: double.infinity,
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
InkResponse(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => BillDetailListScreen()),
);
},
child: Row(
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(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
"₹24,800",
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(
fontFamily: "Poppins",
color: AppColors.subtitleText,
fontSize: 12,
fontWeight: FontWeight.w400,
// fontStyle: FontStyle.italic,
),
),
SvgPicture.asset( return Consumer<SubscribeOrderDetailsProvider>(
"assets/svg/line_ic.svg", builder: (context, provider, child) {
color: AppColors.subtitleText, return SafeArea(
height: 18, top: false,
width: 18, child: Scaffold(
), appBar: AppBar(
// After spacer automaticallyImplyLeading: false,
Row( backgroundColor: Colors.white,
mainAxisAlignment: MainAxisAlignment.spaceBetween, title: Row(
children: [ children: [
InkResponse(
onTap: () => Navigator.pop(context, true),
child: SvgPicture.asset(
"assets/svg/continue_left_ic.svg",
height: 25,
),
),
const SizedBox(width: 10),
const Text(
"Bill List",
style: TextStyle(
fontSize: 16,
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w600,
color: Colors.black87,
),
),
],
),
),
body: _buildBody(provider, screenHeight, bottomPadding),
),
);
},
);
}
Column( Widget _buildBody(SubscribeOrderDetailsProvider provider, double screenHeight, double bottomPadding) {
crossAxisAlignment: CrossAxisAlignment.start, if (provider.isLoading) {
children: [ return const Center(
Text( child: CircularProgressIndicator(),
"Plan", );
style: TextStyle( }
fontFamily: "Poppins",
color: AppColors.subtitleText,
fontWeight: FontWeight.w400,
fontStyle: FontStyle.normal,
fontSize: 12,
),
),
Text(
"₹3600",
style: const TextStyle(
fontFamily: "Poppins",
color: Color(0xFF008CDE),
fontSize: 18,
fontStyle: FontStyle.normal,
fontWeight: FontWeight.w500,
),
),
],
),
Column( if (provider.errorMessage.isNotEmpty) {
crossAxisAlignment: CrossAxisAlignment.start, return Center(
children: [ child: Column(
Text( mainAxisAlignment: MainAxisAlignment.center,
"Deposit", children: [
style: TextStyle( Text(
fontFamily: "Poppins", provider.errorMessage,
color: AppColors.subtitleText, style: const TextStyle(
fontWeight: FontWeight.w400, fontSize: 16,
fontStyle: FontStyle.normal, fontFamily: "Poppins",
fontSize: 12, color: Colors.red,
), ),
), ),
Text( const SizedBox(height: 16),
"₹5483", ElevatedButton(
style: const TextStyle( onPressed: _loadOrderDetails,
fontFamily: "Poppins", child: const Text('Retry'),
color: AppColors.cardAmountText, ),
fontSize: 18, ],
fontStyle: FontStyle.normal, ),
fontWeight: FontWeight.w500, );
), }
),
],
),
Column( if (provider.orderDetails == null) {
crossAxisAlignment: CrossAxisAlignment.start, return const Center(
children: [ child: Text(
Text( 'No order details found',
"Tenure", style: TextStyle(
style: TextStyle( fontSize: 16,
fontFamily: "Poppins", fontFamily: "Poppins",
color: AppColors.subtitleText, color: Colors.grey,
fontWeight: FontWeight.w400, ),
fontStyle: FontStyle.normal, ),
fontSize: 12, );
), }
),
Text(
"6 Months",
style: const TextStyle(
fontFamily: "Poppins",
color: AppColors.cardAmountText,
fontSize: 18,
fontStyle: FontStyle.normal,
fontWeight: FontWeight.w500,
),
),
],
)
],
)
],
),
),
const SizedBox(height: 18), final order = provider.orderDetails!;
Container( return Container(
width: double.infinity, color: const Color(0xFFF3F3F3),
color: Colors.transparent, height: screenHeight,
child: Column( child: SingleChildScrollView(
crossAxisAlignment: CrossAxisAlignment.start, child: Column(
children: [ children: [
const SizedBox(height: 2), // Order header (not in card)
SectionHeading(title: "Generator Overview", padding: EdgeInsets.symmetric(vertical: 4),), Container(
Text( width: double.infinity,
"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.", margin: const EdgeInsets.all(16),
style: TextStyle( padding: const EdgeInsets.all(16),
fontFamily: "Poppins", child: Column(
color: AppColors.subtitleText, crossAxisAlignment: CrossAxisAlignment.center,
fontWeight: FontWeight.w400, children: [
fontStyle: FontStyle.normal, Row(
fontSize: 12, mainAxisAlignment: MainAxisAlignment.center,
), children: [
), const Text(
"# Order ID ",
style: TextStyle(
fontSize: 24,
fontFamily: "Poppins",
fontWeight: FontWeight.w400,
color: Colors.black87,
),
),
Text(
"#${order.orderNum ?? order.orderid ?? 'N/A'}",
style: TextStyle(
fontSize: 24,
fontFamily: "Poppins",
fontWeight: FontWeight.w400,
color: AppColors.amountText,
),
),
],
),
const SizedBox(height: 8),
Text(
order.rentedDate ?? 'Date not available',
style: const TextStyle(
fontSize: 14,
fontFamily: "Poppins",
fontWeight: FontWeight.w400,
color: Colors.grey,
),
),
const SizedBox(height: 14),
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(
fontSize: 12,
fontFamily: "Poppins",
fontWeight: FontWeight.w500,
color: Colors.white,
),
),
),
],
),
),
], // Products section
) if (order.products != null && order.products!.isNotEmpty)
), Container(
const SizedBox(height: 20), width: double.infinity,
margin: const EdgeInsets.symmetric(horizontal: 2),
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
"Products",
style: TextStyle(
fontSize: 18,
fontFamily: "Poppins",
fontWeight: FontWeight.w600,
color: Colors.black87,
),
),
const SizedBox(height: 16),
// Feature cards grid // Product list using ListView.builder
Container( ListView.separated(
width: double.infinity, physics: const NeverScrollableScrollPhysics(),
color: Colors.transparent, shrinkWrap: true,
child: Column( itemCount: order.products!.length,
children: [ separatorBuilder: (context, index) => const SizedBox(height: 16),
// First row of feature cards itemBuilder: (context, index) {
Row( return _buildProductItem(order.products![index]);
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),
// Subscribed Products Card
Expanded(
child: _buildFeatureCard(
title: "1500 RPM",
description: "ENGINE SPEED",
icon: "assets/svg/rpm_ic.svg",
color: Colors.blue,
),
),
],
),
const SizedBox(height: 16), const SizedBox(height: 16),
// Divider
const Divider(
color: Color(0xFFE5E5E5),
thickness: 1,
),
const SizedBox(height: 16),
// Second row of feature cards // Help section
Row( InkResponse(
children: [ onTap: () => _showReasonBottomSheet(),
// Have Complaints Card child: Row(
Expanded( children: [
child: _buildFeatureCard( Text(
title: "75 dB", "Need help with this order?",
description: "LOW NOISE LEVEL", style: TextStyle(
icon: "assets/svg/noise_ic.svg", fontSize: 14,
color: Colors.green, fontFamily: "Poppins",
), fontWeight: FontWeight.w500,
), color: Colors.grey,
const SizedBox(width: 16), ),
// Know Your Payments Card ),
Expanded( ],
child: _buildFeatureCard( ),
title: "24 Hr", ),
description: "LONGER RUNTIME", const SizedBox(height: 16),
icon: "assets/svg/clock_ic.svg",
color: Colors.purple,
),
),
],
),
const SizedBox(height: 20), // View Bill button
], SizedBox(
), width: double.infinity,
), child: ElevatedButton(
], onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => BillDetailListScreen())
);
// 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),
),
elevation: 0,
),
child: const Text(
"View Bill",
style: TextStyle(
fontSize: 16,
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w600,
), ),
), ),
], ),
), ),
], ],
), ),
], )
), 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,
), ),
), ),
], const SizedBox(height: 12),
),
); // Table-like layout for dates and price
} Table(
void showPaymentBottomSheet(BuildContext context) { columnWidths: const {
showModalBottomSheet( 0: FlexColumnWidth(2),
context: context, 1: FlexColumnWidth(1),
isScrollControlled: true, },
backgroundColor: Colors.transparent, children: [
builder: (BuildContext context) { TableRow(
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: [ children: [
// Header - Drag handle TableCell(
Center( child: Text(
child: Container( product.dispatchDate != null ?
width: 40, "Dispatched On ${product.dispatchDate!}" :
height: 4, "Dispatch date not available",
decoration: BoxDecoration( style: const TextStyle(
color: Colors.grey.shade300, fontSize: 12,
borderRadius: BorderRadius.circular(2), fontFamily: "Poppins",
fontWeight: FontWeight.w400,
color: Colors.grey,
), ),
), ),
), ),
const SizedBox(height: 20), const TableCell(
child: Align(
// Pay Amount Section alignment: Alignment.centerRight,
Row( child: Text(
mainAxisAlignment: MainAxisAlignment.spaceBetween, "Plan",
children: [ style: TextStyle(
const Column( fontSize: 12,
crossAxisAlignment: CrossAxisAlignment.start, fontFamily: "Poppins",
children: [ fontWeight: FontWeight.w500,
Text( color: Colors.black87,
"Pay", ),
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w400,
color: Color(0xFF777777),
),
),
SizedBox(height: 4),
Text(
"₹4218",
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.w600,
color: Colors.black,
),
),
],
),
// Rent Amount Section
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
const Text(
"Rent Amount",
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w400,
color: Color(0xFF777777),
),
),
const SizedBox(height: 4),
GestureDetector(
onTap: () {
// Handle view bill details
},
child: Text(
"View Bill Details",
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Color(0xFF008CDE),
),
),
),
],
), ),
], ),
), ),
],
const SizedBox(height: 10), ),
const Divider(height: 1, color: Color(0xFFEEEEEE)), TableRow(
const SizedBox(height: 18), children: [
TableCell(
// Continue Payment Button child: Text(
SizedBox( product.receivedDate != null ?
width: double.infinity, "Received On ${product.receivedDate!}" :
child: ElevatedButton( "Receive date not available",
onPressed: () { style: const TextStyle(
// Handle continue payment fontSize: 12,
}, fontFamily: "Poppins",
style: ElevatedButton.styleFrom( fontWeight: FontWeight.w400,
backgroundColor: const Color(0xFF008CDE), color: Colors.grey,
foregroundColor: Colors.white,
disabledBackgroundColor: const Color(0xFF266E99),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30),
),
padding: const EdgeInsets.symmetric(vertical: 16),
), ),
child: Padding( ),
padding: const EdgeInsets.symmetric(horizontal: 22), ),
child: Row( TableCell(
mainAxisAlignment: MainAxisAlignment.spaceBetween, child: Align(
children: [ alignment: Alignment.centerRight,
const Text( child: Text(
"Continue Payment", product.totalPrice != null ?
style: TextStyle( "${product.totalPrice!}/${product.per ?? 'mo'}" :
color: Color(0xFFFFFFFF), 'Price not available',
fontSize: 16, style: const TextStyle(
), fontSize: 14,
), fontFamily: "Poppins",
SvgPicture.asset( fontWeight: FontWeight.w600,
"assets/svg/continue_ic.svg", color: Colors.black87,
color: Color(0xFFFFFFFF),
height: 25,
width: 25,
),
],
), ),
), ),
), ),
), ),
], ],
), ),
],
),
],
),
);
}
Color _getExpiringColor(String? colorString) {
if (colorString == null || colorString.isEmpty) {
return const Color(0xFFFFEBEB); // Default color
}
try {
// Assuming colorString is in format like "FFFF5757"
return Color(int.parse('FF$colorString', radix: 16));
} catch (e) {
return const Color(0xFFFFEFEF); // Default color on error
}
}
void _showReasonBottomSheet() {
// Your existing bottom sheet implementation
showModalBottomSheet(
context: context,
backgroundColor: Colors.white,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20),
topRight: Radius.circular(20),
),
),
builder: (context) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 14),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
"Select Your Reason",
style: TextStyle(
fontSize: 18,
fontFamily: "Poppins",
fontWeight: FontWeight.w600,
color: Colors.black87,
),
),
const SizedBox(height: 24),
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,
);
},
), ),
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: [ children: [
SizedBox(height: 4,), _buildBalanceCard(data),
// Balance Card const SizedBox(height: 16),
Container(
width: double.infinity, /// 🗓️ Monthly Grouped Transactions
padding: const EdgeInsets.symmetric(vertical: 24, horizontal: 16), ...data.transactions!.entries.map((entry) {
decoration: BoxDecoration( final month = entry.key;
borderRadius: BorderRadius.circular(20), final items = entry.value;
gradient: const LinearGradient( return Column(
colors: [ crossAxisAlignment: CrossAxisAlignment.start,
Color(0xFFE9FFEF),
Color(0xFFD6FFDF),
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
),
child: Column(
children: [ children: [
const SizedBox(height: 12),
Text( Text(
"₹12,596", month,
style: TextStyle( style: const TextStyle(
fontSize: 15,
fontFamily: "Poppins", fontFamily: "Poppins",
fontWeight: FontWeight.w500, fontWeight: FontWeight.w600,
fontSize: 34, color: Colors.black,
color: AppColors.cardAmountText,
), ),
), ),
const SizedBox(height: 4), const SizedBox(height: 8),
Text( ...items.map((txn) => _buildTransactionItem(
"Balance Amount", 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: [
Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(vertical: 24, horizontal: 18),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Color(0xFFF3F3F3),
Color(0xFFB5FFD1),
],
),
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.03),
blurRadius: 8,
offset: const Offset(0, 2),
)
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
/// Header
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
"Balance Amount",
style: TextStyle(
fontFamily: "Poppins",
fontSize: 13,
fontWeight: FontWeight.w500,
color: Colors.redAccent,
),
),
TextButton(
onPressed: () {},
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),
),
const SizedBox(height: 14),
// Credited / Debited info Text(
Row( "₹$balance",
mainAxisAlignment: MainAxisAlignment.spaceEvenly, style: const TextStyle(
children: [ fontFamily: "Poppins",
_buildAmountSummary( fontWeight: FontWeight.w600,
icon: "assets/svg/cross_down_arrow.svg", 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),
/// Credit / Debit Row
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
// ✅ Bill Paid
Row(
children: [
Container(
height: 46,
width: 46,
decoration: BoxDecoration(
color: Colors.white, color: Colors.white,
amount: "₹26,300", borderRadius: BorderRadius.circular(22),
label: "Credited Amount",
), ),
_buildAmountSummary( child: Center(
icon: "assets/svg/cross_up_arrow.svg", child: SvgPicture.asset(
color: Colors.white, "assets/svg/cross_down_arrow.svg",
amount: "₹26,300", height: 18,
label: "Debited Amount", width: 18,
fit: BoxFit.contain, // Ensures the SVG scales within bounds
),
), ),
], ),
),
],
),
),
const SizedBox(height: 16), const SizedBox(width: 10),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"₹$credit",
style: const TextStyle(
fontFamily: "Poppins",
fontWeight: FontWeight.w500,
fontSize: 13,
color: Colors.black,
),
),
Text(
"Bill Paid",
style: TextStyle(
fontFamily: "Poppins",
fontSize: 11,
color: Colors.black54,
),
),
],
),
],
),
const SizedBox(width: 10),
// Transaction list // ❌ Bill Pending
Column( Row(
children: transactions.map((t) { children: [
return _buildTransactionItem( Container(
type: t["type"], height: 46,
title: t["title"], width: 46,
date: t["date"], decoration: BoxDecoration(
amount: t["amount"], color: Colors.white,
ref: t["ref"], borderRadius: BorderRadius.circular(22),
); ),
}).toList(), child: Center(
child: SvgPicture.asset(
"assets/svg/cross_up_arrow.svg",
height: 18,
width: 18,
fit: BoxFit.contain, // Ensures the SVG scales within bounds
),
),
),
const SizedBox(width: 10),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"₹$debit",
style: const TextStyle(
fontFamily: "Poppins",
fontWeight: FontWeight.w500,
fontSize: 13,
color: Colors.black,
),
),
Text(
"Bill Pending",
style: TextStyle(
fontFamily: "Poppins",
fontSize: 11,
color: Colors.black54,
),
)
],
),
],
),
],
), ),
], ],
), ),
), ),
),
);
}
Widget _buildAmountSummary({ /// White overlay card (unchanged)
required String icon,
required Color color,
required String amount,
required String label,
}) {
return Row(
children: [
// Icon and title row
Container( Container(
width: 50, width: double.infinity,
height: 50, padding: const EdgeInsets.all(20),
decoration: BoxDecoration( decoration: BoxDecoration(
color: color.withOpacity(0.9), color: Colors.white,
borderRadius: BorderRadius.circular(30), borderRadius: BorderRadius.circular(16),
), ),
child: Center( // Add this Center widget child: Column(
child: SvgPicture.asset( crossAxisAlignment: CrossAxisAlignment.start,
icon, children: [
height: 20, Row(
width: 20, children: [
// Remove fit: BoxFit.none const Text(
), "Balance Amount",
), style: TextStyle(
), fontFamily: "Poppins",
const SizedBox(width: 8), color: Color(0xFFF00000),
Column( fontSize: 14,
crossAxisAlignment: CrossAxisAlignment.start, fontWeight: FontWeight.w400,
children: [ ),
Text( ),
amount, const SizedBox(width: 4),
style: TextStyle( SvgPicture.asset(
fontFamily: "Poppins", "assets/svg/continue_ic.svg",
fontWeight: FontWeight.w400, color: const Color(0xFFF00000),
fontSize: 14, height: 18,
color: Colors.black, width: 18,
),
],
), ),
), const SizedBox(height: 8),
Text( Row(
label, mainAxisAlignment: MainAxisAlignment.spaceBetween,
style: TextStyle( children: [
fontFamily: "Poppins", Text(
fontWeight: FontWeight.w400, "₹$balance",
fontSize: 12, style: const TextStyle(
color: Colors.grey.shade600, fontFamily: "Poppins",
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(
"*Make sure to pay before you incur any fines.",
style: TextStyle(
fontFamily: "Poppins",
color: Colors.grey.shade500,
fontSize: 12,
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( child: SvgPicture.asset(
color: isCredit ? Colors.green.withOpacity(0.09) : Colors.red.withOpacity(0.09), isCredit
borderRadius: BorderRadius.circular(30), ? "assets/svg/cross_down_arrow.svg"
), : "assets/svg/cross_up_arrow.svg",
child: Center( // Add this Center widget height: 18,
child: SvgPicture.asset(
isCredit ? "assets/svg/cross_down_arrow.svg" : "assets/svg/cross_up_arrow.svg",
height: 20,
width: 20,
// Remove fit: BoxFit.none
),
), ),
), ),
// 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,51 +414,23 @@ class _TransactionsScreenState extends State<TransactionsScreen> { ...@@ -308,51 +414,23 @@ 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( Text(
crossAxisAlignment: CrossAxisAlignment.end, amount,
children: [ style: const TextStyle(
Text( fontFamily: "Poppins",
amount, fontSize: 14,
style: TextStyle( color: Colors.black,
fontFamily: "Poppins", ),
fontWeight: FontWeight.w400,
fontSize: 14,
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