Commit f3a137b2 authored by Sai Srinivas's avatar Sai Srinivas Committed by Sai Srinivas
Browse files

12-11-2025 generator Details Screen and sub screens

parent dd6e2bd2
<svg width="25" height="25" viewBox="0 0 25 25" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M24.2449 17.7605C24.2573 17.6726 19.5713 13.8442 18.0874 14.2192C17.4019 14.373 16.98 14.866 16.1392 15.8474C16.0549 15.9456 15.8989 16.1279 15.7327 16.3169C15.6414 16.4211 15.5215 16.4961 15.3879 16.5327C15.2543 16.5693 15.1129 16.5658 14.9812 16.5227C14.6516 16.4143 14.3277 16.2891 14.0108 16.1477C11.6624 15.1267 9.78878 13.2528 8.76808 10.9043C8.62636 10.5878 8.50119 10.2641 8.39308 9.93457C8.34992 9.80288 8.34634 9.66142 8.3828 9.52771C8.41925 9.39401 8.49413 9.27394 8.59815 9.18237C8.78785 9.01538 8.97169 8.85791 9.07276 8.77149C10.0505 7.93506 10.5449 7.51245 10.6973 6.82617C11.0635 5.34961 7.24391 0.658447 7.15601 0.669434C6.7898 0.244629 6.22071 0.00439453 5.61793 0C4.10328 0.0783692 0.0654383 5.3335 0.00171762 6.2959C-0.00633902 6.33106 -0.0136634 7.6875 0.668221 10.0027C1.37135 12.2966 2.76368 15.5479 6.02223 18.8987C9.37013 22.1536 12.6192 23.5444 14.9116 24.2454C17.2254 24.9265 18.5825 24.9192 18.6177 24.9111C19.5801 24.8438 24.8359 20.8037 24.9165 19.2927C24.9041 18.6929 24.6668 18.1245 24.2449 17.7605Z" fill="url(#paint0_linear_1066_355)"/>
<defs>
<linearGradient id="paint0_linear_1066_355" x1="20.8347" y1="24.0286" x2="-0.247304" y2="2.94727" gradientUnits="userSpaceOnUse">
<stop stop-color="#13B601"/>
<stop offset="0.57" stop-color="#13B601"/>
<stop offset="1" stop-color="#CBF4B4"/>
</linearGradient>
</defs>
</svg>
<svg width="42" height="42" viewBox="0 0 42 42" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="21" cy="21" r="20.5434" stroke="#1487C9" stroke-width="0.913206"/>
<g clip-path="url(#clip0_335_3130)">
<path d="M30.6512 22.5513L23.8542 19.1922C23.7932 19.1622 23.7213 19.1658 23.664 19.2012C23.6062 19.2369 23.5712 19.3 23.5712 19.3678V21.406H23.2622C21.9389 21.406 20.7139 21.8264 19.7061 22.5358V15.6327H21.7165C21.7844 15.6327 21.8474 15.5977 21.8833 15.5401C21.9191 15.4827 21.9223 15.4108 21.8921 15.3499L18.5346 8.5528C18.5018 8.48611 18.4334 8.44385 18.3591 8.44385C18.2847 8.44385 18.2164 8.48611 18.1834 8.5528L14.8227 15.3498C14.7927 15.4107 14.7963 15.4827 14.8321 15.54C14.8678 15.5977 14.9305 15.6327 14.9984 15.6327H17.0641C17.0641 15.6327 17.0641 26.8763 17.0641 33.5558H19.7061C19.7061 29.3844 19.7061 27.6041 19.7061 27.6041C19.7061 25.6432 21.3014 24.0478 23.2622 24.0478H23.5712V26.0867C23.5712 26.1546 23.6061 26.2176 23.6639 26.253C23.7212 26.2889 23.7931 26.2925 23.8542 26.2624L30.6512 22.9026C30.7178 22.8696 30.7601 22.8013 30.7601 22.7269C30.7601 22.6525 30.7179 22.5842 30.6512 22.5513Z" fill="#1487C9"/>
</g>
<defs>
<clipPath id="clip0_335_3130">
<rect width="25.112" height="25.112" fill="white" transform="translate(10.2253 8.44385)"/>
</clipPath>
</defs>
</svg>
......@@ -10,7 +10,7 @@ class CommonResponse {
error = int.tryParse(json['error']?.toString() ?? '');
balance = int.tryParse(json['balance']?.toString() ?? '');
message = json['message']?.toString();
filePath = json['file_path']?.toString();
filePath = json['bill_filepath']?.toString();
}
Map<String, dynamic> toJson() {
......@@ -18,7 +18,7 @@ class CommonResponse {
'error': error,
'balance': balance,
'message': message,
'file_path': filePath,
'bill_filepath': filePath,
};
}
}
......
class GetInTouchListResponse {
String? error;
List<GetInTouchList>? getInTouchList;
String? message;
String? sessionExists;
GetInTouchListResponse(
{this.error, this.getInTouchList, this.message, this.sessionExists});
GetInTouchListResponse.fromJson(Map<String, dynamic> json) {
error = json['error'];
if (json['get_in_touch_list'] != null) {
getInTouchList = <GetInTouchList>[];
json['get_in_touch_list'].forEach((v) {
getInTouchList!.add(new GetInTouchList.fromJson(v));
});
}
message = json['message'];
sessionExists = json['session_exists'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['error'] = this.error;
if (this.getInTouchList != null) {
data['get_in_touch_list'] =
this.getInTouchList!.map((v) => v.toJson()).toList();
}
data['message'] = this.message;
data['session_exists'] = this.sessionExists;
return data;
}
}
class GetInTouchList {
String? id;
String? branchName;
String? googleReviewLink;
String? address;
String? loc;
String? stateName;
String? telephoneNo;
String? mail;
String? latitude;
String? longitude;
GetInTouchList(
{this.id,
this.branchName,
this.googleReviewLink,
this.address,
this.loc,
this.stateName,
this.telephoneNo,
this.mail,
this.latitude,
this.longitude});
GetInTouchList.fromJson(Map<String, dynamic> json) {
id = json['id'];
branchName = json['branch_name'];
googleReviewLink = json['google_review_link'];
address = json['address'];
loc = json['loc'];
stateName = json['state_name'];
telephoneNo = json['telephone_no'];
mail = json['mail'];
latitude = json['latitude'];
longitude = json['longitude'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['branch_name'] = this.branchName;
data['google_review_link'] = this.googleReviewLink;
data['address'] = this.address;
data['loc'] = this.loc;
data['state_name'] = this.stateName;
data['telephone_no'] = this.telephoneNo;
data['mail'] = this.mail;
data['latitude'] = this.latitude;
data['longitude'] = this.longitude;
return data;
}
}
class ProfileDataResponse {
String? error;
Details? details;
String? message;
String? sessionExists;
ProfileDataResponse(
{this.error, this.details, this.message, this.sessionExists});
ProfileDataResponse.fromJson(Map<String, dynamic> json) {
error = json['error'];
details =
json['details'] != null ? new Details.fromJson(json['details']) : null;
message = json['message'];
sessionExists = json['session_exists'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['error'] = this.error;
if (this.details != null) {
data['details'] = this.details!.toJson();
}
data['message'] = this.message;
data['session_exists'] = this.sessionExists;
return data;
}
}
class Details {
String? name;
String? mobNum;
String? email;
String? address;
String? profileImg;
String? state;
String? locality;
Details(
{this.name,
this.mobNum,
this.email,
this.address,
this.profileImg,
this.state,
this.locality});
Details.fromJson(Map<String, dynamic> json) {
name = json['name'];
mobNum = json['mob_num'];
email = json['email'];
address = json['address'];
profileImg = json['profile_img'];
state = json['state'];
locality = json['locality'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['name'] = this.name;
data['mob_num'] = this.mobNum;
data['email'] = this.email;
data['address'] = this.address;
data['profile_img'] = this.profileImg;
data['state'] = this.state;
data['locality'] = this.locality;
return data;
}
}
class amcQuotationListResponse {
String? error;
List<AmcQuotations>? amcQuotations;
String? message;
amcQuotationListResponse({this.error, this.amcQuotations, this.message});
amcQuotationListResponse.fromJson(Map<String, dynamic> json) {
error = json['error'];
if (json['amc_quotations'] != null) {
amcQuotations = <AmcQuotations>[];
json['amc_quotations'].forEach((v) {
amcQuotations!.add(new AmcQuotations.fromJson(v));
});
}
message = json['message'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['error'] = this.error;
if (this.amcQuotations != null) {
data['amc_quotations'] =
this.amcQuotations!.map((v) => v.toJson()).toList();
}
data['message'] = this.message;
return data;
}
}
class AmcQuotations {
String? id;
String? noOfVisits;
String? price;
String? amcStatus;
String? expNote;
String? noOfOilServices;
String? purchaseDate;
AmcQuotations(
{this.id,
this.noOfVisits,
this.price,
this.amcStatus,
this.expNote,
this.noOfOilServices,
this.purchaseDate});
AmcQuotations.fromJson(Map<String, dynamic> json) {
id = json['id'];
noOfVisits = json['no_of_visits'];
price = json['price'];
amcStatus = json['amc_status'];
expNote = json['exp_note'];
noOfOilServices = json['no_of_oil_services'];
purchaseDate = json['purchase_Date'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['no_of_visits'] = this.noOfVisits;
data['price'] = this.price;
data['amc_status'] = this.amcStatus;
data['exp_note'] = this.expNote;
data['no_of_oil_services'] = this.noOfOilServices;
data['purchase_Date'] = this.purchaseDate;
return data;
}
}
class complaintListResponse {
String? error;
List<ComplaintList>? complaintList;
String? message;
complaintListResponse({this.error, this.complaintList, this.message});
complaintListResponse.fromJson(Map<String, dynamic> json) {
error = json['error'];
if (json['complaint_list'] != null) {
complaintList = <ComplaintList>[];
json['complaint_list'].forEach((v) {
complaintList!.add(new ComplaintList.fromJson(v));
});
}
message = json['message'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['error'] = this.error;
if (this.complaintList != null) {
data['complaint_list'] =
this.complaintList!.map((v) => v.toJson()).toList();
}
data['message'] = this.message;
return data;
}
}
class ComplaintList {
String? id;
String? openStatus;
String? modelName;
String? registredDate;
String? hashId;
String? productName;
String? complaintName;
ComplaintList(
{this.id,
this.openStatus,
this.modelName,
this.registredDate,
this.hashId,
this.productName,
this.complaintName});
ComplaintList.fromJson(Map<String, dynamic> json) {
id = json['id'];
openStatus = json['open_status'];
modelName = json['model_name'];
registredDate = json['registred_date'];
hashId = json['hash_id'];
productName = json['product_name'];
complaintName = json['complaint_name'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['open_status'] = this.openStatus;
data['model_name'] = this.modelName;
data['registred_date'] = this.registredDate;
data['hash_id'] = this.hashId;
data['product_name'] = this.productName;
data['complaint_name'] = this.complaintName;
return data;
}
}
class generatorDetailsResponse {
String? error;
String? message;
String? id;
String? hashId;
String? engine;
String? prodName;
String? prodImg;
String? amc;
String? warranty;
GenDetails? genDetails;
LocationDetails? locationDetails;
List<Schedule>? schedule;
List<Quotations>? quotations;
List<AmcQuotations>? amcQuotations;
List<Complaints>? complaints;
generatorDetailsResponse(
{this.error,
this.message,
this.id,
this.hashId,
this.engine,
this.prodName,
this.prodImg,
this.amc,
this.warranty,
this.genDetails,
this.locationDetails,
this.schedule,
this.quotations,
this.amcQuotations,
this.complaints});
generatorDetailsResponse.fromJson(Map<String, dynamic> json) {
error = json['error'];
message = json['message'];
id = json['id'];
hashId = json['hash_id'];
engine = json['engine'];
prodName = json['prod_name'];
prodImg = json['prod_img'];
amc = json['amc'];
warranty = json['warranty'];
genDetails = json['gen_details'] != null
? new GenDetails.fromJson(json['gen_details'])
: null;
locationDetails = json['location_details'] != null
? new LocationDetails.fromJson(json['location_details'])
: null;
if (json['schedule'] != null) {
schedule = <Schedule>[];
json['schedule'].forEach((v) {
schedule!.add(new Schedule.fromJson(v));
});
}
if (json['quotations'] != null) {
quotations = <Quotations>[];
json['quotations'].forEach((v) {
quotations!.add(new Quotations.fromJson(v));
});
}
if (json['amc_quotations'] != null) {
amcQuotations = <AmcQuotations>[];
json['amc_quotations'].forEach((v) {
amcQuotations!.add(new AmcQuotations.fromJson(v));
});
}
if (json['complaints'] != null) {
complaints = <Complaints>[];
json['complaints'].forEach((v) {
complaints!.add(new Complaints.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['error'] = this.error;
data['message'] = this.message;
data['id'] = this.id;
data['hash_id'] = this.hashId;
data['engine'] = this.engine;
data['prod_name'] = this.prodName;
data['prod_img'] = this.prodImg;
data['amc'] = this.amc;
data['warranty'] = this.warranty;
if (this.genDetails != null) {
data['gen_details'] = this.genDetails!.toJson();
}
if (this.locationDetails != null) {
data['location_details'] = this.locationDetails!.toJson();
}
if (this.schedule != null) {
data['schedule'] = this.schedule!.map((v) => v.toJson()).toList();
}
if (this.quotations != null) {
data['quotations'] = this.quotations!.map((v) => v.toJson()).toList();
}
if (this.amcQuotations != null) {
data['amc_quotations'] =
this.amcQuotations!.map((v) => v.toJson()).toList();
}
if (this.complaints != null) {
data['complaints'] = this.complaints!.map((v) => v.toJson()).toList();
}
return data;
}
}
class GenDetails {
String? purchaseDate;
String? altNo;
String? modelName;
String? batterNo;
String? commisDate;
String? dispDate;
GenDetails(
{this.purchaseDate,
this.altNo,
this.modelName,
this.batterNo,
this.commisDate,
this.dispDate});
GenDetails.fromJson(Map<String, dynamic> json) {
purchaseDate = json['purchase_date'];
altNo = json['alt_no'];
modelName = json['model_name'];
batterNo = json['batter_no'];
commisDate = json['commis_date'];
dispDate = json['disp_date'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['purchase_date'] = this.purchaseDate;
data['alt_no'] = this.altNo;
data['model_name'] = this.modelName;
data['batter_no'] = this.batterNo;
data['commis_date'] = this.commisDate;
data['disp_date'] = this.dispDate;
return data;
}
}
class LocationDetails {
String? stateName;
String? districtName;
String? address;
LocationDetails({this.stateName, this.districtName, this.address});
LocationDetails.fromJson(Map<String, dynamic> json) {
stateName = json['state_name'];
districtName = json['district_name'];
address = json['address'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['state_name'] = this.stateName;
data['district_name'] = this.districtName;
data['address'] = this.address;
return data;
}
}
class Schedule {
String? id;
String? date;
String? type;
String? complaintType;
String? reason;
String? status;
Schedule(
{this.id,
this.date,
this.type,
this.complaintType,
this.reason,
this.status});
Schedule.fromJson(Map<String, dynamic> json) {
id = json['id'];
date = json['date'];
type = json['type'];
complaintType = json['complaint_type'];
reason = json['reason'];
status = json['status'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['date'] = this.date;
data['type'] = this.type;
data['complaint_type'] = this.complaintType;
data['reason'] = this.reason;
data['status'] = this.status;
return data;
}
}
class Quotations {
String? id;
String? title;
String? filePath;
String? date;
Quotations({this.id, this.title, this.filePath, this.date});
Quotations.fromJson(Map<String, dynamic> json) {
id = json['id'];
title = json['title'];
filePath = json['file_path'];
date = json['date'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['title'] = this.title;
data['file_path'] = this.filePath;
data['date'] = this.date;
return data;
}
}
class AmcQuotations {
String? id;
String? noOfVisits;
String? price;
String? amcStatus;
String? expNote;
String? noOfOilServices;
String? purchaseDate;
AmcQuotations(
{this.id,
this.noOfVisits,
this.price,
this.amcStatus,
this.expNote,
this.noOfOilServices,
this.purchaseDate});
AmcQuotations.fromJson(Map<String, dynamic> json) {
id = json['id'];
noOfVisits = json['no_of_visits'];
price = json['price'];
amcStatus = json['amc_status'];
expNote = json['exp_note'];
noOfOilServices = json['no_of_oil_services'];
purchaseDate = json['purchase_Date'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['no_of_visits'] = this.noOfVisits;
data['price'] = this.price;
data['amc_status'] = this.amcStatus;
data['exp_note'] = this.expNote;
data['no_of_oil_services'] = this.noOfOilServices;
data['purchase_Date'] = this.purchaseDate;
return data;
}
}
class Complaints {
String? id;
String? openStatus;
String? modelName;
String? registredDate;
String? hashId;
String? productName;
String? complaintName;
Complaints(
{this.id,
this.openStatus,
this.modelName,
this.registredDate,
this.hashId,
this.productName,
this.complaintName});
Complaints.fromJson(Map<String, dynamic> json) {
id = json['id'];
openStatus = json['open_status'];
modelName = json['model_name'];
registredDate = json['registred_date'];
hashId = json['hash_id'];
productName = json['product_name'];
complaintName = json['complaint_name'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['open_status'] = this.openStatus;
data['model_name'] = this.modelName;
data['registred_date'] = this.registredDate;
data['hash_id'] = this.hashId;
data['product_name'] = this.productName;
data['complaint_name'] = this.complaintName;
return data;
}
}
class quotationListResponse {
String? error;
List<ServiceQuotation>? serviceQuotation;
String? message;
quotationListResponse({this.error, this.serviceQuotation, this.message});
quotationListResponse.fromJson(Map<String, dynamic> json) {
error = json['error'];
if (json['service_quotation'] != null) {
serviceQuotation = <ServiceQuotation>[];
json['service_quotation'].forEach((v) {
serviceQuotation!.add(new ServiceQuotation.fromJson(v));
});
}
message = json['message'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['error'] = this.error;
if (this.serviceQuotation != null) {
data['service_quotation'] =
this.serviceQuotation!.map((v) => v.toJson()).toList();
}
data['message'] = this.message;
return data;
}
}
class ServiceQuotation {
String? id;
String? title;
String? date;
String? fileLoc;
ServiceQuotation({this.id, this.title, this.date, this.fileLoc});
ServiceQuotation.fromJson(Map<String, dynamic> json) {
id = json['id'];
title = json['title'];
date = json['date'];
fileLoc = json['file_loc'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['title'] = this.title;
data['date'] = this.date;
data['file_loc'] = this.fileLoc;
return data;
}
}
class scheduleListResponse {
String? error;
List<AllScheduleServices>? allScheduleServices;
String? message;
scheduleListResponse({this.error, this.allScheduleServices, this.message});
scheduleListResponse.fromJson(Map<String, dynamic> json) {
error = json['error'];
if (json['all_schedule_services'] != null) {
allScheduleServices = <AllScheduleServices>[];
json['all_schedule_services'].forEach((v) {
allScheduleServices!.add(new AllScheduleServices.fromJson(v));
});
}
message = json['message'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['error'] = this.error;
if (this.allScheduleServices != null) {
data['all_schedule_services'] =
this.allScheduleServices!.map((v) => v.toJson()).toList();
}
data['message'] = this.message;
return data;
}
}
class AllScheduleServices {
String? id;
String? dueDate;
String? complaintType;
String? comType;
String? status;
AllScheduleServices(
{this.id, this.dueDate, this.complaintType, this.comType, this.status});
AllScheduleServices.fromJson(Map<String, dynamic> json) {
id = json['id'];
dueDate = json['due_date'];
complaintType = json['complaint_type'];
comType = json['com_type'];
status = json['status'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['due_date'] = this.dueDate;
data['complaint_type'] = this.complaintType;
data['com_type'] = this.comType;
data['status'] = this.status;
return data;
}
}
......@@ -139,16 +139,30 @@ class AuthProvider with ChangeNotifier {
}
/// 🔹 Logout API
Future<void> logout(String accId, String sessionId) async {
Future<bool> logout(String accId, String sessionId) async {
_setLoading(true);
try {
final res = await ApiCalling.logoutApi(accId, sessionId);
_logoutResponse = res;
notifyListeners();
// Defensive check — handle both string and int
final errorValue = res?.error?.toString().trim();
if (res != null && errorValue == "0") {
debugPrint("✅ Logout success: ${res.message ?? 'Success'}");
return true;
} else {
debugPrint("❌ Logout failed: ${res?.message ?? 'Unknown error'}");
return false;
}
} catch (e) {
debugPrint("❌ logout Provider Error: $e");
debugPrint("❌ Logout Provider Error: $e");
return false;
} finally {
_setLoading(false);
}
}
}
import 'package:flutter/foundation.dart';
import '../Models/GetInTouchListResponse.dart';
import '../Services/api_calling.dart';
class ContactUsProvider extends ChangeNotifier {
/// 🔹 State Variables
bool _isLoading = false;
String? _errorMessage;
GetInTouchListResponse? _response;
/// 🔹 Getters
bool get isLoading => _isLoading;
String? get errorMessage => _errorMessage;
GetInTouchListResponse? get response => _response;
List<GetInTouchList>? get contactList => _response?.getInTouchList;
/// 🔹 Internal Setter for Loading State
void _setLoading(bool value) {
_isLoading = value;
notifyListeners();
}
/// 🔹 Fetch Contact Us / Get In Touch List API
Future<void> fetchContactUs(String accId, String sessionId) async {
_setLoading(true);
_errorMessage = null;
try {
debugPrint("📡 Fetching Get In Touch list...");
final res = await ApiCalling.fetchGetInTouchListApi(accId, sessionId);
_response = res;
if (res == null) {
_errorMessage = "No response from server.";
} else if (res.error?.toString().trim() != "0") {
_errorMessage = res.message?.trim().isNotEmpty == true
? res.message
: "Something went wrong. Please try again.";
}
} catch (e) {
debugPrint("❌ ContactUsProvider Error: $e");
_errorMessage = "An unexpected error occurred. Please try again.";
} finally {
_setLoading(false);
}
}
/// 🔹 Optional: Refresh list (shortcut method)
Future<void> refresh(String accId, String sessionId) async {
await fetchContactUs(accId, sessionId);
}
/// 🔹 Clear data (useful when logging out or leaving screen)
void clearData() {
_response = null;
_errorMessage = null;
notifyListeners();
}
}
import 'package:flutter/material.dart';
import 'package:gen_service/Models/ProfileDataResponse.dart';
import 'package:gen_service/Services/api_calling.dart';
import '../Models/DashboardResponse.dart';
......@@ -48,6 +49,38 @@ class DashboardProvider extends ChangeNotifier {
}
}
// Fetch Profile provider
// =======================
ProfileDataResponse? _profileData;
ProfileDataResponse? get profileData => _profileData;
Future<void> fetchProfile(String accId, String sessionId) async {
_isLoading = true;
_errorMessage = null;
notifyListeners();
try {
final response = await ApiCalling.fetchProfileDataApi(accId, sessionId);
if (response != null) {
if (response.error?.toString() == "0") {
_profileData = response;
} else {
_errorMessage = response.message ?? "Something went wrong!";
}
} else {
_errorMessage = "No response from server.";
}
} catch (e) {
debugPrint("❌ fetchProfile error: $e");
_errorMessage = "Failed to fetch profile data: ${e.toString()}";
} finally {
_isLoading = false;
notifyListeners();
}
}
// =======================
// Refresh Dashboard
// =======================
......@@ -64,4 +97,6 @@ class DashboardProvider extends ChangeNotifier {
_isLoading = false;
notifyListeners();
}
}
import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:gen_service/Services/api_calling.dart';
import 'package:open_filex/open_filex.dart';
import 'package:path_provider/path_provider.dart';
import '../Models/TransactionModels/BillDetailResponse.dart';
import '../Models/TransactionModels/PaymentDetailResponse.dart';
import '../Models/TransactionModels/TransactionListResponse.dart';
import '../Services/api_post_request.dart';
import '../Utility/CustomSnackbar.dart';
class TransactionsProvider with ChangeNotifier {
bool _isLoading = false;
......@@ -108,6 +114,132 @@ class TransactionsProvider with ChangeNotifier {
notifyListeners();
}
bool _isDownloading = false;
bool get isDownloading => _isDownloading;
void setDownloading(bool value) {
_isDownloading = value;
notifyListeners();
}
/// Download Bill
Future<void> downloadBill(BuildContext context,
String sessionId, String billId, String accId) async {
setDownloading(true);
try {
// Step 1: Call billDownloadApi to get file path
final res = await ApiCalling.billDownloadApi(sessionId, billId, accId);
if (res == null || res.error != 0 || res.filePath == null) {
// showToast(res?.message ?? "Download failed");
setDownloading(false);
return;
}
final fileUrl = res.filePath!;
final dir = await getApplicationDocumentsDirectory();
final savePath = '${dir.path}/receipt_${billId}.pdf';
// Step 2: Download file
final success = await downloadFile(fileUrl, savePath);
if (success) {
CustomSnackBar.showSuccess(
context: context,
message: "File downloaded successfully!"
);
// showToast("File downloaded successfully!");
await OpenFilex.open(savePath);
} else {
CustomSnackBar.showError(
context: context,
message: "Failed to download file"
);
// showToast("Failed to download file");
}
} catch (e) {
debugPrint("❌ Bill download error: $e");
CustomSnackBar.showError(
context: context,
message: "Error downloading file"
);
// showToast("Error downloading file");
} finally {
setDownloading(false);
}
}
/// File Downloader
Future<bool> downloadFile(String apiUrl, String savePath) async {
try {
final response = await get(apiUrl, {}); // Using your get() helper
if (response != null && response.statusCode == 200) {
final file = File(savePath);
await file.writeAsBytes(response.bodyBytes);
return true;
} else {
debugPrint("❌ Failed to download file: ${response?.statusCode}");
return false;
}
} catch (e) {
debugPrint("❌ File download error: $e");
return false;
}
}
/// Download Payment Receipt
Future<void> downloadPaymentReceipt(
BuildContext context,
String sessionId,
String ledgerId,
String accId,
) async {
setDownloading(true);
try {
// Step 1: Call paymentReceiptDownloadApi to get file URL/path
final res = await ApiCalling.paymentReceiptDownloadApi(sessionId, ledgerId, accId);
if (res == null || res.error != 0 || res.filePath == null) {
CustomSnackBar.showError(
context: context,
message: res?.message ?? "Download failed",
);
setDownloading(false);
return;
}
final fileUrl = res.filePath!;
final dir = await getApplicationDocumentsDirectory();
final savePath = '${dir.path}/payment_receipt_$ledgerId.pdf';
// Step 2: Download file using helper
final success = await downloadFile(fileUrl, savePath);
if (success) {
CustomSnackBar.showSuccess(
context: context,
message: "Payment receipt downloaded successfully!",
);
await OpenFilex.open(savePath);
} else {
CustomSnackBar.showError(
context: context,
message: "Failed to download payment receipt",
);
}
} catch (e) {
debugPrint("❌ Payment receipt download error: $e");
CustomSnackBar.showError(
context: context,
message: "Error downloading payment receipt",
);
} finally {
setDownloading(false);
}
}
// ---------------------------------------------------------------------------
// 🔹 Clear All Data (optional helper)
void clearAll() {
......
import 'package:flutter/foundation.dart';
import 'package:gen_service/Models/amcQuotationListResponse.dart';
import 'package:gen_service/Models/complaintListResponse.dart';
import 'package:gen_service/Models/generatorDetailsResponse.dart';
import 'package:gen_service/Models/quotationListResponse.dart';
import 'package:gen_service/Models/scheduleListResponse.dart';
import '../Services/api_calling.dart';
class Generatordetailsprovider extends ChangeNotifier{
generatorDetailsResponse? _detailsResponse;
scheduleListResponse? _scheduleResponse;
quotationListResponse? _quotationResponse;
complaintListResponse? _complaintResponse;
amcQuotationListResponse? _amcQuotationResponse;
bool _isLoading = false;
String? _errorMessage;
bool _showMoreDetails = false;
generatorDetailsResponse? get detailsResponse => _detailsResponse;
scheduleListResponse? get scheduleResponse => _scheduleResponse;
quotationListResponse? get quotationResponse => _quotationResponse;
complaintListResponse? get complaintResponse => _complaintResponse;
amcQuotationListResponse? get amcQuotationResponse => _amcQuotationResponse;
bool get isLoading => _isLoading;
String? get errorMessage => _errorMessage;
bool get showMoreDetails => _showMoreDetails;
set showMoreDetails(bool value) {
_showMoreDetails = value;
notifyListeners();
}
Future<void> fetchGeneratorDetails(String accId, String sessionId,genId) async {
_isLoading = true;
_errorMessage = null;
notifyListeners();
try {
final response = await ApiCalling.generatorDetailsAPI(accId, sessionId,genId);
if (response != null) {
if (response.error == "0") {
_detailsResponse = response;
} else {
_errorMessage = response.message ?? "Something went wrong!";
}
} else {
_errorMessage = "No response from server.";
}
} catch (e) {
_errorMessage = "Failed to fetch dashboard: $e";
} finally {
_isLoading = false;
notifyListeners();
}
}
Future<void> fetchScheduleList(String accId, String sessionId,gen_id) async {
_isLoading = true;
_errorMessage = null;
notifyListeners();
try {
final response = await ApiCalling.scheduleListAPI(accId, sessionId,gen_id);
if (response != null) {
if (response.error == "0") {
_scheduleResponse = response;
notifyListeners();
} else {
_errorMessage = response.message ?? "Something went wrong!";
}
} else {
_errorMessage = "No response from server.";
}
} catch (e) {
_errorMessage = "Failed to fetch dashboard: $e";
} finally {
_isLoading = false;
notifyListeners();
}
}
Future<void> fetchQuotationList(String accId, String sessionId,genID) async {
_isLoading = true;
_errorMessage = null;
notifyListeners();
try {
final response = await ApiCalling.quotationListAPI(accId, sessionId,genID);
if (response != null) {
if (response.error == "0") {
_quotationResponse = response;
notifyListeners();
} else {
_errorMessage = response.message ?? "Something went wrong!";
}
} else {
_errorMessage = "No response from server.";
}
} catch (e) {
_errorMessage = "Failed to fetch dashboard: $e";
} finally {
_isLoading = false;
notifyListeners();
}
}
Future<void> fetchComplaintList(String accId, String sessionId) async {
_isLoading = true;
_errorMessage = null;
notifyListeners();
try {
final response = await ApiCalling.complaintListAPI(accId, sessionId);
if (response != null) {
if (response.error == "0") {
_complaintResponse = response;
notifyListeners();
} else {
_errorMessage = response.message ?? "Something went wrong!";
}
} else {
_errorMessage = "No response from server.";
}
} catch (e) {
_errorMessage = "Failed to fetch dashboard: $e";
} finally {
_isLoading = false;
notifyListeners();
}
}
Future<void> fetchAmcQuotationList(String accId, String sessionId,genId) async {
_isLoading = true;
_errorMessage = null;
notifyListeners();
try {
final response = await ApiCalling.amcQuoteListAPI(accId, sessionId,genId);
if (response != null) {
if (response.error == "0") {
_amcQuotationResponse = response;
notifyListeners();
} else {
_errorMessage = response.message ?? "Something went wrong!";
}
} else {
_errorMessage = "No response from server.";
}
} catch (e) {
_errorMessage = "Failed to fetch dashboard: $e";
} finally {
_isLoading = false;
notifyListeners();
}
}
}
\ No newline at end of file
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:gen_service/Notifiers/ContactUsProvider.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart';
import '../Utility/AppColors.dart';
class ContactUsScreen extends StatefulWidget {
final String accId;
final String sessionId;
const ContactUsScreen({
Key? key,
required this.accId,
required this.sessionId,
}) : super(key: key);
@override
State<ContactUsScreen> createState() => _ContactUsScreenState();
}
class _ContactUsScreenState extends State<ContactUsScreen> {
bool _stretch = true;
@override
void initState() {
super.initState();
Future.microtask(() {
final getInTouchProvider =
Provider.of<ContactUsProvider>(context, listen: false);
getInTouchProvider.fetchContactUs(widget.accId, widget.sessionId);
});
}
@override
Widget build(BuildContext context) {
final getInTouchProvider = Provider.of<ContactUsProvider>(context);
final isLoading = getInTouchProvider.isLoading;
final error = getInTouchProvider.errorMessage;
final data = getInTouchProvider.response?.getInTouchList;
if (isLoading) {
return const Scaffold(
backgroundColor: AppColors.backgroundRegular,
body: Center(
child: CircularProgressIndicator(color: AppColors.buttonColor),
),
);
}
if (error != null) {
return Scaffold(
backgroundColor: AppColors.backgroundRegular,
body: Center(
child: Text(
error,
style: const TextStyle(color: Colors.red, fontSize: 16),
),
),
);
}
if (data == null || data.isEmpty) {
return const Scaffold(
backgroundColor: AppColors.backgroundRegular,
body: Center(
child: Text("No data found."),
),
);
}
return RefreshIndicator.adaptive(
color: AppColors.amountText,
onRefresh: () async {
await Future.delayed(const Duration(milliseconds: 600));
getInTouchProvider.fetchContactUs(widget.accId, widget.sessionId);
},
child: Scaffold(
backgroundColor: const Color(0xFF4076FF),
body: CustomScrollView(
physics: const ClampingScrollPhysics(),
slivers: <Widget>[
SliverAppBar(
leading: Container(),
stretch: _stretch,
backgroundColor: const Color(0xFF4076FF),
onStretchTrigger: () async {
final provider =
Provider.of<ContactUsProvider>(context, listen: false);
provider.fetchContactUs(widget.accId, widget.sessionId);
},
stretchTriggerOffset: 300.0,
expandedHeight: 245.0,
flexibleSpace: FlexibleSpaceBar(
stretchModes: const [
StretchMode.zoomBackground,
StretchMode.blurBackground,
],
background: Container(
width: double.infinity,
decoration:
const BoxDecoration(gradient: AppColors.backgroundGradient),
child: SafeArea(
bottom: false,
child: Padding(
padding: const EdgeInsets.only(
top: 20, bottom: 25, left: 20, right: 20),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: const [
SizedBox(height: 16),
SizedBox(height: 8),
Text(
"Map view!.....",
style: TextStyle(fontSize: 22, color: Colors.white),
),
SizedBox(height: 12),
SizedBox(height: 2),
],
),
),
),
),
),
),
// Body content
SliverToBoxAdapter(
child: Container(
padding: const EdgeInsets.only(top: 1, bottom: 0),
color: Colors.transparent,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 0),
decoration: const BoxDecoration(
color: AppColors.backgroundRegular,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(30),
topRight: Radius.circular(30),
),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 20),
/// Build dynamic branch list
for (var item in data)
Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: _buildItemRow(
branchName: item.branchName ?? "Unknown Branch",
address: item.address ?? "No address available",
phoneNo: item.telephoneNo ?? "",
lat: item.latitude ?? "",
long: item.longitude ?? "",
),
),
const SizedBox(height: 30),
],
),
),
),
),
),
],
),
),
);
}
/// Branch card widget
Widget _buildItemRow({
required String branchName,
required String address,
required String phoneNo,
required String lat,
required String long,
}) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 14),
decoration: BoxDecoration(
color: Colors.white, borderRadius: BorderRadius.circular(22)),
child: Row(
children: [
const SizedBox(width: 14),
Expanded(
flex: 6,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
branchName,
style: const TextStyle(
color: AppColors.normalText,
fontWeight: FontWeight.w600,
fontSize: 14,
overflow: TextOverflow.ellipsis,
),
),
const SizedBox(height: 4),
Text(
address,
maxLines: 4,
style: const TextStyle(
color: AppColors.subtitleText,
fontWeight: FontWeight.w400,
fontSize: 14,
overflow: TextOverflow.ellipsis,
),
),
],
),
),
const SizedBox(width: 8),
Row(
children: [
InkResponse(
onTap: () {
// map lat & long
},
child: Container(
padding: const EdgeInsets.all(1),
decoration: BoxDecoration(
color: Colors.transparent,
borderRadius: BorderRadius.circular(30)),
child: SvgPicture.asset(
"assets/svg/route_ic.svg",
height: 42,
fit: BoxFit.contain,
),
),
),
const SizedBox(width: 6),
InkResponse(
onTap: () async {
final phone = phoneNo.trim();
if (phone.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
backgroundColor: Colors.redAccent,
content: const Text(
"Phone number not available",
style: TextStyle(color: Colors.white),
),
duration: Duration(seconds: 2),
behavior: SnackBarBehavior.floating,
),
);
return;
}
final Uri phoneUri = Uri(scheme: 'tel', path: phone);
try {
if (await canLaunchUrl(phoneUri)) {
await launchUrl(phoneUri);
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
backgroundColor: Colors.redAccent,
content: const Text(
"Unable to start the call",
style: TextStyle(color: Colors.white),
),
duration: Duration(seconds: 2),
behavior: SnackBarBehavior.floating,
),
);
}
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
backgroundColor: Colors.redAccent,
content: Text(
"Error while trying to call: $e",
style: const TextStyle(color: Colors.white),
),
duration: const Duration(seconds: 2),
behavior: SnackBarBehavior.floating,
),
);
}
},
child: Container(
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
color: const Color(0xFF4CAF50),
borderRadius: BorderRadius.circular(30),
),
child: SvgPicture.asset(
"assets/svg/phone_ic.svg",
height: 16,
color: Colors.white,
fit: BoxFit.contain,
),
),
),
],
),
],
),
);
}
}
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:gen_service/Notifiers/HelpAndComplaintProvider.dart';
import 'package:gen_service/Screens/HelpAndComplaintScreens/SelectOrderHelpScreen.dart';
import 'package:gen_service/Screens/ProfileScreen.dart';
import 'package:provider/provider.dart';
import '../../Utility/AppColors.dart';
class ComplaintListScreen extends StatefulWidget {
final String accId;
final String sessionId;
const ComplaintListScreen({
Key? key,
required this.accId,
required this.sessionId,
}) : super(key: key);
@override
State<ComplaintListScreen> createState() => _ComplaintListScreenState();
}
class _ComplaintListScreenState extends State<ComplaintListScreen> {
bool _stretch = true;
@override
void initState() {
super.initState();
Future.microtask(() {
final provider =
Provider.of<HelpAndComplaintProvider>(context, listen: false);
provider.fetchComplaintsList(
accId: widget.accId,
sessionId: widget.sessionId,
);
});
}
@override
Widget build(BuildContext context) {
final provider = Provider.of<HelpAndComplaintProvider>(context);
final isLoading = provider.isLoading;
final error = provider.errorMessage;
final data = provider.complaintListResponse;
if (isLoading) {
return const Scaffold(
backgroundColor: AppColors.backgroundRegular,
body: Center(
child: CircularProgressIndicator(color: AppColors.buttonColor),
),
);
}
if (error != null) {
return Scaffold(
backgroundColor: AppColors.backgroundRegular,
body: Center(
child: Text(
error,
style: const TextStyle(color: Colors.red, fontSize: 16),
),
),
);
}
if (data == null) {
return const Scaffold(
backgroundColor: AppColors.backgroundRegular,
body: Center(
child: Text("No data found."),
),
);
}
// Separate open and closed complaints
final openComplaints = data.complaintList!
.where((c) => c.openStatus?.toLowerCase() == "open")
.toList();
final closedComplaints = data.complaintList!
.where((c) => c.openStatus?.toLowerCase() == "closed")
.toList();
return RefreshIndicator.adaptive(
color: AppColors.amountText,
onRefresh: () async {
await provider.fetchComplaintsList(
accId: widget.accId,
sessionId: widget.sessionId,
);
},
child: Scaffold(
backgroundColor: AppColors.backgroundRegular,
body: CustomScrollView(
physics: const ClampingScrollPhysics(),
slivers: <Widget>[
SliverAppBar(
leading: Container(),
stretch: _stretch,
backgroundColor: const Color(0xFF4076FF),
onStretchTrigger: () async {
await provider.fetchComplaintsList(
accId: widget.accId,
sessionId: widget.sessionId,
);
},
stretchTriggerOffset: 300.0,
// expandedHeight: 60.0,
flexibleSpace: FlexibleSpaceBar(
stretchModes: const [
StretchMode.zoomBackground,
StretchMode.blurBackground,
],
background: Container(
decoration:
const BoxDecoration(color: AppColors.primary),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
InkResponse(
onTap: () => Navigator.pop(context, true),
child: SvgPicture.asset(
color: Colors.white,
"assets/svg/continue_left_ic.svg",
height: 30,
),
),
const SizedBox(width: 10),
const Text(
"Help?",
style: TextStyle(
fontSize: 16,
fontFamily: "Poppins",
fontWeight: FontWeight.w400,
color: Colors.white,
),
),
],
),
),
),
),
),
/// Main body container
SliverToBoxAdapter(
child: Container(
padding: const EdgeInsets.only(top: 1),
color: AppColors.primary,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20),
decoration: const BoxDecoration(
color: AppColors.backgroundRegular,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(30),
topRight: Radius.circular(30),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
/// Top Tile
InkResponse(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context)=> SelectOrderHelpScreen(accId: widget.accId, sessionId: widget.sessionId))
).then((_) async {
await provider.fetchComplaintsList(
accId: widget.accId,
sessionId: widget.sessionId,
);
});
},
child: Container(
padding: const EdgeInsets.symmetric(
vertical: 16, horizontal: 14),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(14),
),
child: Row(
children: [
SizedBox(
height: 42,
width: 42,
child: SvgPicture.asset(
"assets/svg/help_ic.svg",
fit: BoxFit.contain,
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
Text(
"Get help for an order",
style: TextStyle(
fontFamily: "Poppins",
fontSize: 14,
fontWeight: FontWeight.w400,
color: Colors.black,
),
),
SizedBox(height: 4),
Text(
"Select a Generator",
style: TextStyle(
fontFamily: "Poppins",
fontSize: 12,
fontWeight: FontWeight.w400,
color: Colors.grey,
),
),
],
),
),
SvgPicture.asset(
"assets/svg/continue_ic.svg",
color: Colors.black,
height: 32,
),
],
),
),
),
const SizedBox(height: 25),
/// Raised Complaints
if (openComplaints.isNotEmpty)
const Text(
"Raised Complaints",
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Colors.black87,
),
),
const SizedBox(height: 8),
...openComplaints.map(
(c) => ComplaintCard(
title: c.complaintName ?? "-",
id: c.id ?? "-",
product: c.productName ?? "",
status: c.openStatus ?? "Open",
date: c.registredDate ?? "",
engModel: c.modelName ?? "-",
),
),
const SizedBox(height: 25),
/// Resolved Complaints
if (closedComplaints.isNotEmpty)
const Text(
"Resolved Complaints",
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Colors.black87,
),
),
const SizedBox(height: 8),
...closedComplaints.map(
(c) => ComplaintCard(
title: c.complaintName ?? "-",
id: c.id ?? "",
product: c.productName ?? "",
status: c.openStatus ?? "Closed",
date: c.registredDate ?? "",
engModel: c.modelName ?? "",
),
),
],
),
),
),
),
],
),
),
);
}
}
/// Reusable Complaint Item Card
class ComplaintCard extends StatelessWidget {
final String title;
final String id;
final String product;
final String status;
final String date;
final String engModel;
const ComplaintCard({
super.key,
required this.title,
required this.id,
required this.product,
required this.status,
required this.date,
required this.engModel,
});
@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.symmetric(vertical: 6),
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 14),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.grey.shade200),
boxShadow: [
BoxShadow(
color: Colors.grey.shade200,
blurRadius: 4,
offset: const Offset(0, 2),
)
],
),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
"#${id} | ",
style: TextStyle(
fontFamily: "Poppins",
fontSize: 14,
fontWeight: FontWeight.w400,
color: AppColors.amountText,
),
),
Text(
title,
style: TextStyle(
fontFamily: "Poppins",
fontSize: 14,
fontWeight: FontWeight.w400,
color: AppColors.amountText,
),
),
],
),
const SizedBox(height: 4),
Text(
"Date: $date",
style: TextStyle(
fontFamily: "Poppins",
fontSize: 12,
fontWeight: FontWeight.w400,
color: AppColors.subtitleText,
),
),
const SizedBox(height: 2),
],
),
Container(
padding: EdgeInsets.symmetric(horizontal: 15, vertical: 7),
decoration: BoxDecoration(
color: status == "Open" ? AppColors.successBG : AppColors.yellowBG,
borderRadius: BorderRadius.circular(10),
),
child: Text(
status,
style: TextStyle(
fontFamily: "Poppins",
fontSize: 14,
fontWeight: FontWeight.w400,
color: status == "Open" ? AppColors.success : AppColors.normalText,
),
),
),
],
),
Divider(),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
flex: 6,
child: Text(
product,
maxLines: 2,
style: TextStyle(
fontFamily: "Poppins",
fontSize: 12,
fontWeight: FontWeight.w400,
color: Colors.black87,
),
),
),
Text(
"#${id} | Engine :$engModel",
style: TextStyle(
fontFamily: "Poppins",
fontSize: 12,
fontWeight: FontWeight.w400,
color: AppColors.subtitleText,
),
),
],
),
],
),
);
}
}
......@@ -26,7 +26,10 @@ class _SelectOrderHelpScreenState extends State<SelectOrderHelpScreen> {
void initState() {
super.initState();
Future.microtask(() {
final provider = Provider.of<HelpAndComplaintProvider>(context, listen: false);
final provider = Provider.of<HelpAndComplaintProvider>(
context,
listen: false,
);
provider.fetchGeneratorList(
accId: widget.accId,
sessionId: widget.sessionId,
......@@ -63,12 +66,12 @@ class _SelectOrderHelpScreenState extends State<SelectOrderHelpScreen> {
);
}
if (generatorData == null || generatorData.orders == null || generatorData.orders!.isEmpty) {
if (generatorData == null ||
generatorData.orders == null ||
generatorData.orders!.isEmpty) {
return const Scaffold(
backgroundColor: AppColors.backgroundRegular,
body: Center(
child: Text("No Generators Found."),
),
body: Center(child: Text("No Generators Found.")),
);
}
......@@ -107,7 +110,10 @@ class _SelectOrderHelpScreenState extends State<SelectOrderHelpScreen> {
background: Container(
decoration: const BoxDecoration(color: AppColors.primary),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 16,
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
......@@ -141,7 +147,10 @@ class _SelectOrderHelpScreenState extends State<SelectOrderHelpScreen> {
child: Container(
color: AppColors.primary,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20),
padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 20,
),
decoration: const BoxDecoration(
color: AppColors.backgroundRegular,
borderRadius: BorderRadius.only(
......@@ -166,21 +175,24 @@ class _SelectOrderHelpScreenState extends State<SelectOrderHelpScreen> {
),
const SizedBox(height: 16),
/// 🧾 Generator List from Provider
/// Generator List from Provider
...genList.map((order) {
return Column(
children: [
InkResponse(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => AddComplaintScreen(
accId: widget.accId,
sessionId: widget.sessionId,
product: order.prodName.toString(),
hashId: order.hashId.toString(),
modolNo: order.engine.toString())
)
context,
MaterialPageRoute(
builder:
(context) => AddComplaintScreen(
accId: widget.accId,
sessionId: widget.sessionId,
product: order.prodName.toString(),
hashId: order.hashId.toString(),
modolNo: order.engine.toString(),
),
),
).then((_) async {
await provider.fetchGeneratorList(
accId: widget.accId,
......@@ -189,17 +201,20 @@ class _SelectOrderHelpScreenState extends State<SelectOrderHelpScreen> {
});
},
child: _buildOrderItem(
assetId: "#${order.hashId ?? ''} | Engine : ${order.engine ?? ''}",
assetId:
"#${order.hashId ?? ''} | Engine : ${order.engine ?? ''}",
description: order.prodName ?? '',
amc: order.amc ?? '',
warranty: order.warranty ?? '',
pImage: order.productImage ?? '',
date: order.schedule?.isNotEmpty == true
? order.schedule!.first
: null,
serviceText: order.schedule?.isNotEmpty == true
? 'Upcoming Service Scheduled'
: null,
date:
order.schedule?.isNotEmpty == true
? order.schedule!.first
: null,
serviceText:
order.schedule?.isNotEmpty == true
? 'Upcoming Service Scheduled'
: null,
),
),
const SizedBox(height: 12),
......@@ -270,11 +285,15 @@ class _SelectOrderHelpScreenState extends State<SelectOrderHelpScreen> {
children: [
if (amc == "1" || amc == "2")
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 4,
),
decoration: BoxDecoration(
gradient: amc == "1"
? AppColors.greenStripGradient
: AppColors.fadeGradient,
gradient:
amc == "1"
? AppColors.greenStripGradient
: AppColors.fadeGradient,
borderRadius: BorderRadius.circular(12),
),
child: Row(
......@@ -283,9 +302,10 @@ class _SelectOrderHelpScreenState extends State<SelectOrderHelpScreen> {
SvgPicture.asset(
"assets/svg/tick_ic.svg",
height: 14,
color: amc == "1"
? AppColors.greenICBg
: AppColors.subtitleText,
color:
amc == "1"
? AppColors.greenICBg
: AppColors.subtitleText,
),
const SizedBox(width: 4),
Text(
......@@ -295,9 +315,10 @@ class _SelectOrderHelpScreenState extends State<SelectOrderHelpScreen> {
fontFamily: "PoppinsBold",
fontStyle: FontStyle.italic,
fontWeight: FontWeight.w700,
color: amc == "1"
? AppColors.greenICBg
: AppColors.subtitleText,
color:
amc == "1"
? AppColors.greenICBg
: AppColors.subtitleText,
),
),
],
......@@ -305,11 +326,15 @@ class _SelectOrderHelpScreenState extends State<SelectOrderHelpScreen> {
),
if (warranty == "1" || warranty == "2")
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 4,
),
decoration: BoxDecoration(
gradient: warranty == "1"
? AppColors.yellowStripGradient
: AppColors.fadeGradient,
gradient:
warranty == "1"
? AppColors.yellowStripGradient
: AppColors.fadeGradient,
borderRadius: BorderRadius.circular(12),
),
child: Row(
......@@ -318,9 +343,10 @@ class _SelectOrderHelpScreenState extends State<SelectOrderHelpScreen> {
SvgPicture.asset(
"assets/svg/tick2_ic.svg",
height: 14,
color: warranty == "1"
? AppColors.warning
: AppColors.subtitleText,
color:
warranty == "1"
? AppColors.warning
: AppColors.subtitleText,
),
const SizedBox(width: 4),
Text(
......@@ -330,9 +356,10 @@ class _SelectOrderHelpScreenState extends State<SelectOrderHelpScreen> {
fontFamily: "PoppinsBold",
fontStyle: FontStyle.italic,
fontWeight: FontWeight.w700,
color: warranty == "1"
? AppColors.normalText
: AppColors.subtitleText,
color:
warranty == "1"
? AppColors.normalText
: AppColors.subtitleText,
),
),
],
......@@ -359,59 +386,60 @@ class _SelectOrderHelpScreenState extends State<SelectOrderHelpScreen> {
height: 50,
width: 50,
fit: BoxFit.contain,
errorBuilder: (context, error, stack) => Image.asset(
'assets/images/dashboard_gen.png',
height: 40,
width: 40,
),
errorBuilder:
(context, error, stack) => Image.asset(
'assets/images/dashboard_gen.png',
height: 40,
width: 40,
),
),
),
],
),
/// Service info (optional)
if (date != null && serviceText != null) ...[
const SizedBox(height: 12),
Container(
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12),
decoration: BoxDecoration(
color: const Color(0xffF2F2F2),
borderRadius: BorderRadius.circular(16),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
SvgPicture.asset(
"assets/svg/checked_ic.svg",
height: 30,
fit: BoxFit.contain,
),
const SizedBox(width: 10),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
date,
style: const TextStyle(
fontSize: 12,
color: AppColors.normalText,
fontWeight: FontWeight.w500,
),
),
Text(
serviceText,
style: const TextStyle(
fontSize: 12,
color: AppColors.subtitleText,
fontWeight: FontWeight.w500,
),
),
],
),
],
),
),
],
// if (date != null && serviceText != null) ...[
// const SizedBox(height: 12),
// Container(
// padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12),
// decoration: BoxDecoration(
// color: const Color(0xffF2F2F2),
// borderRadius: BorderRadius.circular(16),
// ),
// child: Row(
// mainAxisAlignment: MainAxisAlignment.start,
// children: [
// SvgPicture.asset(
// "assets/svg/checked_ic.svg",
// height: 30,
// fit: BoxFit.contain,
// ),
// const SizedBox(width: 10),
// Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Text(
// date,
// style: const TextStyle(
// fontSize: 12,
// color: AppColors.normalText,
// fontWeight: FontWeight.w500,
// ),
// ),
// Text(
// serviceText,
// style: const TextStyle(
// fontSize: 12,
// color: AppColors.subtitleText,
// fontWeight: FontWeight.w500,
// ),
// ),
// ],
// ),
// ],
// ),
// ),
// ],
],
),
);
......
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:gen_service/Screens/ContactUsScreen.dart';
import 'package:gen_service/Screens/HelpAndComplaintScreens/SelectOrderHelpScreen.dart';
import 'package:gen_service/Screens/HelpAndComplaintScreens/ComplaintListScreen.dart';
import 'package:gen_service/Screens/ProfileScreen.dart';
import 'package:gen_service/Screens/TransactionListScreen.dart';
import 'package:gen_service/Screens/TransactionScreens/TransactionListScreen.dart';
import 'package:provider/provider.dart';
import '../Notifiers/DashboardProvider.dart';
import '../Utility/AppColors.dart';
import '../Utility/CustomSnackbar.dart';
import 'generatorDetailsScreen.dart';
class HomeScreen extends StatefulWidget {
final String accId;
final String sessionId;
......@@ -22,9 +31,12 @@ class HomeScreen extends StatefulWidget {
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
bool _stretch = true;
DateTime? currentBackPressTime;
@override
void initState() {
super.initState();
......@@ -35,6 +47,24 @@ class _HomeScreenState extends State<HomeScreen> {
});
}
Future<bool> _onWillPop() async {
DateTime now = DateTime.now();
if (currentBackPressTime == null ||
now.difference(currentBackPressTime!) > Duration(seconds: 2)) {
currentBackPressTime = now;
CustomSnackBar.showExit(
context: context,
title: "Exit",
message: 'Press back again to exit'
);
return false;
}
// Close the entire app immediately
exit(0);
}
@override
Widget build(BuildContext context) {
final dashboardProvider = Provider.of<DashboardProvider>(context);
......@@ -73,259 +103,270 @@ class _HomeScreenState extends State<HomeScreen> {
);
}
return RefreshIndicator.adaptive(
color: AppColors.amountText,
onRefresh: () async {
await Future.delayed(const Duration(milliseconds: 600));
Provider.of<DashboardProvider>(context, listen: false);
dashboardProvider.fetchDashboard(widget.accId, widget.sessionId);
},
child: Scaffold(
backgroundColor: Color(0xFF4076FF),
body: CustomScrollView(
physics: ClampingScrollPhysics(),
slivers: <Widget>[
SliverAppBar(
leading: Container(),
stretch: _stretch,
backgroundColor: Color(0xFF4076FF),
onStretchTrigger: () async {
// Refresh data when pulled down
final dashboardProvider =
Provider.of<DashboardProvider>(context, listen: false);
dashboardProvider.fetchDashboard(widget.accId, widget.sessionId);
},
stretchTriggerOffset: 300.0,
expandedHeight: 232.0,
flexibleSpace: LayoutBuilder(
builder: (context, constraints) {
final top = constraints.biggest.height;
return FlexibleSpaceBar(
stretchModes: const [
StretchMode.zoomBackground,
StretchMode.blurBackground,
],
background: GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProfileScreen(
accId: widget.accId,
sessionId: widget.sessionId,
return WillPopScope(
onWillPop: _onWillPop,
child: RefreshIndicator.adaptive(
color: AppColors.amountText,
onRefresh: () async {
await Future.delayed(const Duration(milliseconds: 600));
Provider.of<DashboardProvider>(context, listen: false);
dashboardProvider.fetchDashboard(widget.accId, widget.sessionId);
},
child: Scaffold(
backgroundColor: Color(0xFF4076FF),
body: CustomScrollView(
physics: ClampingScrollPhysics(),
slivers: <Widget>[
SliverAppBar(
leading: Container(),
stretch: _stretch,
backgroundColor: Color(0xFF4076FF),
onStretchTrigger: () async {
// Refresh data when pulled down
final dashboardProvider =
Provider.of<DashboardProvider>(context, listen: false);
dashboardProvider.fetchDashboard(widget.accId, widget.sessionId);
},
stretchTriggerOffset: 300.0,
expandedHeight: 232.0,
flexibleSpace: LayoutBuilder(
builder: (context, constraints) {
final top = constraints.biggest.height;
return FlexibleSpaceBar(
stretchModes: const [
StretchMode.zoomBackground,
StretchMode.blurBackground,
],
background: GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProfileScreen(
accId: widget.accId,
sessionId: widget.sessionId,
),
),
),
);
},
child: Container(
width: double.infinity,
decoration: const BoxDecoration(gradient: AppColors.backgroundGradient),
child: SafeArea(
bottom: false,
child: Padding(
padding: const EdgeInsets.only(top: 10, bottom: 12, left: 20, right: 20),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// Profile Image
Container(
height: 80,
width: 80,
decoration: const BoxDecoration(
color: Color(0xFFE6F6FF),
shape: BoxShape.circle,
),
clipBehavior: Clip.antiAlias,
child: (data.userProfile?.isNotEmpty == true)
? Image.network(
data.userProfile.toString(),
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) =>
CircleAvatar(
radius: 40,
backgroundColor: const Color(0xFFE0F4FF),
child: SvgPicture.asset(
height: 40,
"assets/svg/person_ic.svg",
fit: BoxFit.contain,
);
},
child: Container(
width: double.infinity,
decoration: const BoxDecoration(gradient: AppColors.backgroundGradient),
child: SafeArea(
bottom: false,
child: Padding(
padding: const EdgeInsets.only(top: 10, bottom: 12, left: 20, right: 20),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// Profile Image
Container(
height: 80,
width: 80,
decoration: const BoxDecoration(
color: Color(0xFFE6F6FF),
shape: BoxShape.circle,
),
clipBehavior: Clip.antiAlias,
child: (data.userProfile?.isNotEmpty == true)
? Image.network(
data.userProfile.toString(),
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) =>
CircleAvatar(
radius: 40,
backgroundColor: const Color(0xFFE0F4FF),
child: SvgPicture.asset(
height: 40,
"assets/svg/person_ic.svg",
fit: BoxFit.contain,
),
),
),
)
: CircleAvatar(
radius: 40,
backgroundColor: const Color(0xFFE0F4FF),
child: SvgPicture.asset(
height: 40,
"assets/svg/person_ic.svg",
fit: BoxFit.contain,
)
: CircleAvatar(
radius: 40,
backgroundColor: const Color(0xFFE0F4FF),
child: SvgPicture.asset(
height: 40,
"assets/svg/person_ic.svg",
fit: BoxFit.contain,
),
),
),
),
const SizedBox(height: 16),
Flexible(
child: Text(
data.userName.toString(),
style: const TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.w600,
const SizedBox(height: 16),
Flexible(
child: Text(
data.userName.toString(),
style: const TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.w600,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
const SizedBox(height: 8),
Text(
'+91 ${data.mobNum}',
style: TextStyle(
color: Colors.white.withOpacity(0.9),
fontSize: 14,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
const SizedBox(height: 8),
Text(
'+91 ${data.mobNum}',
style: TextStyle(
color: Colors.white.withOpacity(0.9),
fontSize: 14,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
)
],
)
],
),
),
),
),
),
),
);
},
);
},
),
),
),
// Body content
SliverToBoxAdapter(
child: Container(
padding: const EdgeInsets.only(top: 1, bottom: 0),
color: Colors.transparent,
// Body content
SliverToBoxAdapter(
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 0),
decoration: const BoxDecoration(
color: AppColors.backgroundRegular,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(30),
topRight: Radius.circular(30),
padding: const EdgeInsets.only(top: 1, bottom: 0),
color: Colors.transparent,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 0),
decoration: const BoxDecoration(
color: AppColors.backgroundRegular,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(30),
topRight: Radius.circular(30),
),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 20),
// Orders Section
_buildOrdersSection(data),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 20),
const SizedBox(height: 4),
// Orders Section
_buildOrdersSection(data),
// Transactions Card
_buildTransactionsCard(data),
const SizedBox(height: 4),
const SizedBox(height: 18),
const Divider(),
// Transactions Card
_buildTransactionsCard(data),
// Complaints Section
_buildComplaintsSection(data),
const SizedBox(height: 18),
const Divider(),
const SizedBox(height: 20),
// Complaints Section
_buildComplaintsSection(data),
InkResponse(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SelectOrderHelpScreen(accId: widget.accId, sessionId: widget.sessionId))
);
},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 14),
child: Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: const Color(0xFFD7F0FF),
borderRadius: BorderRadius.circular(16),
border: Border.all(width: 1.5, color: AppColors.buttonColor),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
Text('Facing Issues?',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: AppColors.amountText)),
Text(
'Raise a ticket to resolve your issues.',
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w400,
color: AppColors.subtitleText),
),
],
),
SvgPicture.asset("assets/svg/requirements.svg",
height: 32, width: 32),
],
),
),
),
),
const SizedBox(height: 20),
const SizedBox(height: 20),
// Get in Touch Card
Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 30),
decoration: BoxDecoration(
color: AppColors.amountText,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(24),
topRight: Radius.circular(24),
),
),
InkResponse(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SelectOrderHelpScreen(accId: widget.accId, sessionId: widget.sessionId))
);
},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Column(
padding: const EdgeInsets.symmetric(horizontal: 14),
child: Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: const Color(0xFFD7F0FF),
borderRadius: BorderRadius.circular(16),
border: Border.all(width: 1.5, color: AppColors.buttonColor),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
children: const [
Text('Facing Issues?',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: AppColors.amountText)),
Text(
'Get in touch With Us',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Colors.white),
),
const SizedBox(height: 4),
Text(
'Please feel free to reach out to us anytime',
'Raise a ticket to resolve your issues.',
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w400,
color: Color(0xAAFFFFFF)),
color: AppColors.subtitleText),
),
],
),
),
const Icon(Icons.arrow_circle_right,
color: Color(0xFFFFFFFF), size: 30),
],
SvgPicture.asset("assets/svg/requirements.svg",
height: 32, width: 32),
],
),
),
)
), // Add bottom padding
],
),
),
const SizedBox(height: 20),
// Get in Touch Card
InkResponse(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => ContactUsScreen(accId: widget.accId, sessionId: widget.sessionId))
);
},
child: Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 30),
decoration: BoxDecoration(
color: AppColors.amountText,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(24),
topRight: Radius.circular(24),
),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Get in touch With Us',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Colors.white),
),
const SizedBox(height: 4),
Text(
'Please feel free to reach out to us anytime',
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w400,
color: Color(0xAAFFFFFF)),
),
],
),
),
const Icon(Icons.arrow_circle_right,
color: Color(0xFFFFFFFF), size: 30),
],
),
)
),
), // Add bottom padding
],
),
),
),
),
),
],
],
),
),
),
);
......@@ -343,35 +384,51 @@ class _HomeScreenState extends State<HomeScreen> {
);
}
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 14),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 16),
...orders.map<Widget>((order) {
return Column(
children: [
_buildOrderItem(
assetId: "#${order.hashId ?? ''} | Engine : ${order.engine ?? ''}",
description: order.prodName ?? '',
amc: (order.amc?.toString() ?? ''),
warranty: (order.warranty?.toString() ?? ''),
pImage: order.productImage ?? '',
date: order.schedule?.isNotEmpty == true
? order.schedule!.first
: null,
serviceText: order.schedule?.isNotEmpty == true
? 'Upcoming Service Scheduled'
: null,
),
const SizedBox(height: 12),
],
);
}).toList(),
// See All Button
return InkResponse(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 14),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 16),
...orders.map<Widget>((order) {
return Column(
children: [
InkResponse(
onTap: () async {
await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Generatordetailsscreen(
accId: widget.accId,
sessionId: widget.sessionId,
genId: order.id,
),
),
);
},
child: _buildOrderItem(
assetId: "#${order.hashId ?? ''} | Engine : ${order.engine ?? ''}",
description: order.prodName ?? '',
amc: (order.amc?.toString() ?? ''),
warranty: (order.warranty?.toString() ?? ''),
pImage: order.productImage ?? '',
date: order.schedule?.isNotEmpty == true
? order.schedule!.first
: null,
serviceText: order.schedule?.isNotEmpty == true
? 'Upcoming Service Scheduled'
: null,
),
),
const SizedBox(height: 12),
],
);
}).toList(),
// See All Button
],
],
),
),
);
}
......@@ -640,7 +697,7 @@ class _HomeScreenState extends State<HomeScreen> {
width: double.infinity,
padding: const EdgeInsets.all(22),
decoration: BoxDecoration(
gradient: AppColors.balanceCardGradientP,
gradient: dashboardData.balanceType == 'Pending Balance'? AppColors.balanceCardGradientP : AppColors.balanceCardGradientA,
borderRadius: BorderRadius.circular(16),
),
child: Row(
......@@ -700,6 +757,7 @@ class _HomeScreenState extends State<HomeScreen> {
),
),
const SizedBox(height: 10),
if (dashboardData.balanceType == 'Pending Balance')
InkResponse(
onTap: () {
// Add pay now logic
......
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:gen_service/Screens/AuthScreen/LoginScreen.dart';
import 'package:gen_service/Utility/CustomSnackbar.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../Notifiers/AuthProvider.dart';
import '../Notifiers/DashboardProvider.dart';
import '../Utility/AppColors.dart';
import '../Utility/SharedpreferencesService.dart';
class ProfileScreen extends StatefulWidget {
final String accId;
......@@ -20,747 +27,623 @@ class ProfileScreen extends StatefulWidget {
}
class _ProfileScreenState extends State<ProfileScreen> {
bool _stretch = true;
@override
void initState() {
super.initState();
Future.microtask(() {
final dashboardProvider =
Provider.of<DashboardProvider>(context, listen: false);
dashboardProvider.fetchDashboard(widget.accId, widget.sessionId);
final profileProvider = Provider.of<DashboardProvider>(context, listen: false);
profileProvider.fetchProfile(widget.accId, widget.sessionId);
});
}
@override
Widget build(BuildContext context) {
final dashboardProvider = Provider.of<DashboardProvider>(context);
final isLoading = dashboardProvider.isLoading;
final error = dashboardProvider.errorMessage;
final data = dashboardProvider.dashboardData;
Future<void> onLogout(BuildContext context) async {
final provider = Provider.of<AuthProvider>(context, listen: false);
return Scaffold(
backgroundColor: AppColors.backgroundRegular,
body: Stack(
children: [
if (isLoading)
const Center(
child: CircularProgressIndicator(),
)
else if (error != null)
Center(
child: Text(
error,
style: const TextStyle(color: Colors.red, fontSize: 16),
),
)
else if (data == null)
const Center(
child: Text("No data found."),
)
else
Stack(
children: [
/// 🔹 Profile Section
Positioned(
top: 0,
left: 0,
right: 0,
child: _buildProfileSection(
data.userProfile ?? "",
data.userName ?? "User",
data.mobNum ?? "",
),
// 🧭 Step 1: Ask confirmation
final confirm = await showDialog<bool>(
context: context,
builder: (context) {
return Dialog(
backgroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
elevation: 0,
child: Container(
padding: const EdgeInsets.all(24),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 20,
offset: const Offset(0, 4),
),
],
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// Icon
Container(
width: 60,
height: 60,
decoration: BoxDecoration(
color: AppColors.buttonColor.withOpacity(0.1),
shape: BoxShape.circle,
),
child: Icon(
Icons.logout_rounded,
color: AppColors.buttonColor,
size: 30,
),
),
const SizedBox(height: 16),
// Title
Text(
"Logout",
style: TextStyle(
fontFamily: "Poppins",
fontWeight: FontWeight.w700,
fontSize: 20,
color: Colors.black87,
),
),
const SizedBox(height: 8),
// Subtitle
Text(
"Are you sure you want to logout?",
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: "Poppins",
fontSize: 14,
fontWeight: FontWeight.w400,
color: Colors.grey[600],
height: 1.4,
),
),
const SizedBox(height: 24),
/// 🔹 Scrollable Content
SafeArea(
child: SingleChildScrollView(
physics: const ClampingScrollPhysics(),
child: Column(
children: [
const SizedBox(height: 200),
/// Bottom Sheet style container
Container(
padding: const EdgeInsets.symmetric(horizontal: 14),
width: double.infinity,
decoration: const BoxDecoration(
color: AppColors.backgroundRegular,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(30),
topRight: Radius.circular(30),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 20),
// 🔹 Orders Section
_buildOrdersSection(data),
const SizedBox(height: 4),
// 🔹 Transactions Card
_buildTransactionsCard(data),
const SizedBox(height: 18),
const Divider(),
// 🔹 Complaints Section
_buildComplaintsSection(data),
const SizedBox(height: 20),
_buildFixIssuesCard(),
const SizedBox(height: 20),
// _buildGetInTouchCard(),
// const SizedBox(height: 30),
],
),
// Buttons Row
Row(
children: [
// Cancel Button
Expanded(
child: OutlinedButton(
onPressed: () => Navigator.pop(context, false),
style: OutlinedButton.styleFrom(
backgroundColor: Colors.transparent,
foregroundColor: Colors.grey[600],
side: BorderSide(
color: Colors.grey[300]!,
width: 1.5,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
padding: const EdgeInsets.symmetric(vertical: 14),
elevation: 0,
),
child: Text(
"Cancel",
style: TextStyle(
fontFamily: "Poppins",
fontWeight: FontWeight.w600,
fontSize: 14,
color: Colors.grey[700],
),
),
),
),
const SizedBox(width: 12),
Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 30),
decoration: BoxDecoration(
color: AppColors.amountText,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(24),
topRight: Radius.circular(24),
// Logout Button
Expanded(
child: ElevatedButton(
onPressed: () => Navigator.pop(context, true),
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.buttonColor,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
padding: const EdgeInsets.symmetric(vertical: 14),
elevation: 0,
shadowColor: Colors.transparent,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Logout",
style: TextStyle(
fontFamily: "Poppins",
fontWeight: FontWeight.w600,
fontSize: 14,
color: Colors.white,
),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Get in touch With Us',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Colors.white),
),
Text(
'Please feel free to reach out to us anytime',
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w400,
color: Color(0xAAFFFFFF)),
),
],
),
Icon(Icons.arrow_circle_right_rounded,
color: Color(0xFFFFFFFF), size: 30),
],
),
)
),
],
],
),
),
),
),
],
),
],
),
],
),
],
),
),
);
},
);
}
// PROFILE SECTION
// ===============================
Widget _buildProfileSection(
String userProfile, String userName, String userContact) {
return Container(
width: double.infinity,
height: 275,
padding: const EdgeInsets.only(top: 60, bottom: 60, left: 20, right: 20),
decoration: const BoxDecoration(gradient: AppColors.backgroundGradient),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// Profile Image
Container(
height: 80,
width: 80,
decoration: const BoxDecoration(
color: Color(0xFFE6F6FF),
shape: BoxShape.circle,
// Step 2: If user cancelled, just return
if (confirm != true) return;
// Step 3: Show loading indicator
showDialog(
context: context,
barrierDismissible: false,
builder: (context) {
return BackdropFilter(
filter: ImageFilter.blur(sigmaX: 3, sigmaY: 3),
child: Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
clipBehavior: Clip.antiAlias,
child: (userProfile.isNotEmpty)
? Image.network(
userProfile,
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) =>
const Icon(Icons.person,
color: Color(0xFF2d2d2d), size: 100),
)
: CircleAvatar(
radius: 80,
backgroundColor: const Color(0xFFE0F4FF),
child: SvgPicture.asset(
height: 80,
"assets/svg/person_ic.svg",
fit: BoxFit.contain,
backgroundColor: Colors.white.withOpacity(0.9),
child: Padding(
padding: const EdgeInsets.all(24),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
CircularProgressIndicator(
color: AppColors.buttonColor,
strokeWidth: 3,
),
const SizedBox(height: 20),
Text(
"Logging out...",
style: TextStyle(
fontFamily: "Poppins",
fontWeight: FontWeight.w600,
fontSize: 16,
color: Colors.grey[800],
),
),
],
),
),
),
const SizedBox(height: 16),
Text(
userName,
style: const TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 8),
Text(
'+91 $userContact',
style: TextStyle(
color: Colors.white.withOpacity(0.9),
fontSize: 14,
),
)
],
),
);
}
// ORDERS SECTION
// ===============================
Widget _buildOrdersSection(dashboardData) {
final orders = dashboardData.orders ?? [];
if (orders.isEmpty) {
return const Center(
child: Text("No Orders Found"),
);
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: orders.map<Widget>((order) {
return Column(
children: [
_buildOrderItem(
assetId: "#${order.hashId ?? ''} | Engine : ${order.engine ?? ''}",
description: order.prodName ?? '',
status: (order.amc?.isNotEmpty ?? false)
? 'AMC Protected'
: (order.warranty?.isNotEmpty ?? false)
? 'WARRANTY'
: 'Unknown',
statusColor: (order.amc?.isNotEmpty ?? false)
? Colors.green
: (order.warranty?.isNotEmpty ?? false)
? Colors.orange
: Colors.grey,
p_imagae: order.productImage ?? '',
date: order.schedule?.isNotEmpty == true
? order.schedule!.first
: null,
serviceText: order.schedule?.isNotEmpty == true
? 'Upcoming Service Scheduled'
: null,
),
const SizedBox(height: 12),
],
);
}).toList(),
},
);
}
Widget _buildOrderItem({
required String assetId,
required String description,
required String status,
required Color statusColor,
required String p_imagae,
String? date,
String? serviceText,
}) {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
assetId,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w400,
color: AppColors.amountText,
),
),
const SizedBox(height: 4),
Text(
description,
style: TextStyle(
fontSize: 14,
color: AppColors.normalText,
height: 1.4,
),
),
try {
final success = await provider.logout(
widget.accId,
widget.sessionId,
);
const SizedBox(height: 8),
// Close loading dialog
if (context.mounted) Navigator.pop(context);
// Status Badge with checkmark for AMC Protected
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: statusColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(6),
border: Border.all(
color: statusColor.withOpacity(0.3),
),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (status == 'AMC Protected')
const Icon(
Icons.check_circle,
color: Colors.green,
size: 12,
),
if (status == 'AMC Protected') const SizedBox(width: 4),
Text(
status,
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.w600,
color: statusColor,
),
),
],
),
),
],
),
),
if (success) {
// ✅ Success — clear shared prefs
final prefs = SharedPreferencesService.instance;
await prefs.clearPreferences();
if (context.mounted) {
CustomSnackBar.showSuccess(
context: context,
message: "Logged out successfully"
);
const SizedBox(width: 12),
await Future.delayed(const Duration(milliseconds: 1500));
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: const Color(0xffF2F2F2),
borderRadius: BorderRadius.circular(12),
),
child: Image.network(
p_imagae ?? "https://erp.gengroup.in/assets/upload/inventory_add_genesis_product_pic/_1761047459_6425.png",
height: 50,
width: 50,
fit: BoxFit.contain,
errorBuilder: (context, error, stack) =>
Image.asset('assets/images/dashboard_gen.png',
height: 40,
width: 40),
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (_) => const LoginScreen()),
(route) => false,
);
}
} else {
// ❌ Logout failed — show error snackbar
if (context.mounted) {
CustomSnackBar.showError(
context: context,
message: "Logout failed. Please try again."
);
}
}
} catch (e) {
// Handle exceptions
if (context.mounted) Navigator.pop(context);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
backgroundColor: Colors.redAccent,
content: Row(
children: [
const Icon(Icons.error_outline, color: Colors.white, size: 20),
const SizedBox(width: 8),
Text(
"Logout failed: ${e.toString()}",
style: const TextStyle(
fontFamily: "Poppins",
fontWeight: FontWeight.w500,
),
),
],
),
// Date and Service Text for first item
if (date != null && serviceText != null) ...[
const SizedBox(height: 12),
Container(
padding: EdgeInsets.symmetric(horizontal: 14, vertical: 12),
decoration: BoxDecoration(
color: const Color(0xffF2F2F2),
borderRadius: BorderRadius.circular(16),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
SvgPicture.asset(
height: 30,
"assets/svg/checked_ic.svg",
fit: BoxFit.contain,
),
SizedBox(width: 10,),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
date,
style: TextStyle(
fontSize: 12,
color: AppColors.normalText,
fontWeight: FontWeight.w500,
),
),
Text(
serviceText,
style: TextStyle(
fontSize: 12,
color: AppColors.subtitleText,
fontWeight: FontWeight.w500,
),
),
],
),
duration: const Duration(seconds: 3),
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
);
}
],
),
),
],
],
),
);
}
// TRANSACTIONS CARD
// ===============================
Widget _buildTransactionsCard(dashboardData) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Transactions',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
color: Colors.black87,
),
),
],
@override
Widget build(BuildContext context) {
final profileProvider = Provider.of<DashboardProvider>(context);
final authProvider = Provider.of<AuthProvider>(context);
final isLoading = profileProvider.isLoading;
final error = profileProvider.errorMessage;
final data = profileProvider.profileData?.details;
if (isLoading) {
return const Scaffold(
backgroundColor: AppColors.backgroundRegular,
body: Center(
child: CircularProgressIndicator(color: AppColors.buttonColor,),
),
const SizedBox(height: 8),
Container(
width: double.infinity,
padding: const EdgeInsets.all(22),
decoration: BoxDecoration(
gradient: AppColors.balanceCardGradientP,
borderRadius: BorderRadius.circular(16),
);
}
if (error != null) {
return Scaffold(
backgroundColor: AppColors.backgroundRegular,
body: Center(
child: Text(
error,
style: const TextStyle(color: Colors.red, fontSize: 16),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"₹",
style: TextStyle(
color: Colors.white,
fontSize: 20,
height: 2,
fontWeight: FontWeight.w500,
),
),
Text(
dashboardData?.balanceAmount?.toString() ?? "0",
style: TextStyle(
color: Colors.white,
fontSize: 34,
fontWeight: FontWeight.w500,
),
),
),
);
}
if (data == null) {
return const Scaffold(
backgroundColor: AppColors.backgroundRegular,
body: Center(
child: Text("No data found."),
),
);
}
return RefreshIndicator.adaptive(
color: AppColors.amountText,
onRefresh: () async {
await Future.delayed(const Duration(milliseconds: 600));
Provider.of<DashboardProvider>(context, listen: false);
profileProvider.fetchDashboard(widget.accId, widget.sessionId);
},
child: Scaffold(
backgroundColor: Color(0xFF4076FF),
body: CustomScrollView(
physics: ClampingScrollPhysics(),
slivers: <Widget>[
SliverAppBar(
leading: Container(),
stretch: _stretch,
backgroundColor: Color(0xFF4076FF),
onStretchTrigger: () async {
// Refresh data when pulled down
final profileProvider =
Provider.of<DashboardProvider>(context, listen: false);
profileProvider.fetchProfile(widget.accId, widget.sessionId);
},
stretchTriggerOffset: 300.0,
expandedHeight: 245.0,
flexibleSpace: LayoutBuilder(
builder: (context, constraints) {
final top = constraints.biggest.height;
return FlexibleSpaceBar(
stretchModes: const [
StretchMode.zoomBackground,
StretchMode.blurBackground,
],
),
const SizedBox(height: 4),
Row(
children: [
const Icon(Icons.info_outline,
color: Colors.white, size: 16),
const SizedBox(width: 6),
Text(
dashboardData.balanceType ?? 'Pending Balance',
style: const TextStyle(
color: Colors.white,
fontSize: 14,
background: GestureDetector(
child: Container(
width: double.infinity,
decoration: const BoxDecoration(gradient: AppColors.backgroundGradient),
child: SafeArea(
bottom: false,
child: Padding(
padding: const EdgeInsets.only(top: 20, bottom: 25, left: 20, right: 20),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// Profile Image
Container(
height: 80,
width: 80,
decoration: const BoxDecoration(
color: Color(0xFFE6F6FF),
shape: BoxShape.circle,
),
clipBehavior: Clip.antiAlias,
child: (data.profileImg?.isNotEmpty == true)
? Image.network(
data.profileImg.toString(),
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) =>
CircleAvatar(
radius: 40,
backgroundColor: const Color(0xFFE0F4FF),
child: SvgPicture.asset(
height: 40,
"assets/svg/person_ic.svg",
fit: BoxFit.contain,
),
),
)
: CircleAvatar(
radius: 40,
backgroundColor: const Color(0xFFE0F4FF),
child: SvgPicture.asset(
height: 40,
"assets/svg/person_ic.svg",
fit: BoxFit.contain,
),
),
),
const SizedBox(height: 16),
Flexible(
child: Text(
data.name.toString(),
style: const TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.w600,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
const SizedBox(height: 8),
Text(
'+91 ${data.mobNum}',
style: TextStyle(
color: Colors.white.withOpacity(0.9),
fontSize: 14,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
SizedBox(height: 12,),
InkResponse(
onTap: () => onLogout(context),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 8),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
),
child: const Text(
" Logout ",
style: TextStyle(
color: AppColors.normalText,
fontWeight: FontWeight.w400,
fontSize: 14,
overflow: TextOverflow.ellipsis,
),
),
),
),
SizedBox(height: 2,),
],
),
),
),
),
],
),
],
),
);
},
),
Column(
children: [
Text(
"*Make sure to pay before\n you incur any fines.",
maxLines: 2,
style: TextStyle(
color: Color(0xAAFFFFFF),
fontSize: 12,
fontWeight: FontWeight.w500,
),
// Body content
SliverToBoxAdapter(
child: Container(
padding: const EdgeInsets.only(top: 1, bottom: 0),
color: Colors.transparent,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 0),
decoration: const BoxDecoration(
color: AppColors.backgroundRegular,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(30),
topRight: Radius.circular(30),
),
),
SizedBox(height: 10,),
InkResponse(
child: Container(
padding:
const EdgeInsets.symmetric(horizontal: 20, vertical: 8),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
),
child: const Text(
" Pay Now ",
style: TextStyle(
color: Colors.blue,
fontFamily: "Poppins",
fontSize: 14,
fontWeight: FontWeight.w500,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 20),
// email
_buildItemRow(
icon: "assets/svg/message_ic.svg",
iconBg: Color(0xFFDFF8FF),
title: "Email ID",
subTitle: data.email.toString()
),
),
const SizedBox(height: 8),
// address
_buildItemRow(
icon: "assets/svg/lolipop_ic.svg",
iconBg: Color(0xFFFFE5E5),
title: "Address",
subTitle: data.address.toString()
),
const SizedBox(height: 4),
// state
_buildItemRow(
icon: "assets/svg/pay_card_ic.svg",
iconBg: Color(0xFFDFF8FF),
title: "State",
subTitle: data.state.toString()
),
const SizedBox(height: 4),
// sub local
_buildItemRow(
icon: "assets/svg/pay_card_ic.svg",
iconBg: Color(0xFFDFF8FF),
title: "Sub Locality",
subTitle: data.locality.toString()
),
const SizedBox(height: 120),
Padding(
padding: const EdgeInsets.only( bottom: 10, left: 20, right: 20),
child: Center(
child: Column(
children: [
SvgPicture.asset(
"assets/svg/genesis_logo_2io.svg",
height: 55,
color: AppColors.buttonColor,
),
SizedBox(height: 12,),
Text(
'Genesis Poweronics PVT. Ltd.',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w400,
color: AppColors.subtitleText),
),
Text(
'App Version 1.0',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w400,
color: AppColors.subtitleText),
),
],
),
),
),
const SizedBox(height: 80),
],
),
),
],
),
),
],
),
),
],
),
],
),
);
}
// COMPLAINTS SECTION
// ===============================
Widget _buildComplaintsSection(dashboardData) {
final complaints = dashboardData.complaints ?? [];
if (complaints.isEmpty) {
return const Center(
child: Text(
"No Complaints Found",
style: TextStyle(fontSize: 14, color: Colors.grey),
),
);
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 8),
const Text(
"Complaints",
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
color: Colors.black87,
),
),
const SizedBox(height: 8),
...complaints.map<Widget>((c) {
return Container(
margin: const EdgeInsets.only(bottom: 10),
padding: const EdgeInsets.all(16),
Widget _buildItemRow({
required String icon,
required Color iconBg,
required String title,
required String subTitle,
}) {
return Container(
padding: EdgeInsets.all(16),
child: Row(
children: [
if(title != "State" && title != "Sub Locality")
Container(
padding: EdgeInsets.all(title =="Address" ? 8 :12),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.grey.shade200),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.03),
blurRadius: 6,
offset: const Offset(0, 2),
color: iconBg,
borderRadius: BorderRadius.circular(18)
),
child: SvgPicture.asset(
height: title =="Address" ? 34: 26,
icon,
fit: BoxFit.contain,
),
),
if(title != "Email ID" && title != "Address")
Expanded(
flex: 1,
child: Container(
padding: EdgeInsets.all(14),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(18)
),
],
child: SizedBox(
width: 24,
height: 24,
),
),
),
SizedBox(width: 14,),
Expanded(
flex: 7,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
/// 🔹 Top row — Complaint name and status
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Complaint info
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"#${c.hashId ?? ''} | ${c.complaintName ?? ''}",
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: AppColors.amountText,
),
),
const SizedBox(height: 4),
Text(
c.registredDate ?? '',
style: TextStyle(
fontSize: 12,
color: Colors.grey.shade600,
),
),
],
),
),
// Status badge
Container(
padding: const EdgeInsets.symmetric(
horizontal: 16, vertical: 8),
decoration: BoxDecoration(
color: (c.openStatus?.toLowerCase() == 'open')
? AppColors.successBG
: AppColors.warningBg2,
borderRadius: BorderRadius.circular(10),
),
child: Text(
c.openStatus ?? '',
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: (c.openStatus?.toLowerCase() == 'open')
? AppColors.success
: AppColors.warningText,
),
),
),
],
Text(
title,
style: TextStyle(
color: AppColors.normalText,
fontWeight: FontWeight.w400,
fontSize: 14,
overflow: TextOverflow.ellipsis,
),
),
const SizedBox(height: 12),
const Divider(),
/// 🔹 Product Info Row
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// Product details
Text(
c.productName ?? "Unknown Product",
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: AppColors.normalText,
),
),
// Product ID
Row(
children: [
Text(
"#${c.id ?? ''} | ",
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color: AppColors.subtitleText,
),
),
if ((c.modelName ?? '').isNotEmpty)
Text(
"Engine: ${c.modelName}",
style: TextStyle(
fontSize: 12,
color: AppColors.subtitleText,
),
),
],
),
],
Text(
subTitle,
maxLines: 4,
style: TextStyle(
color: AppColors.subtitleText,
fontWeight: FontWeight.w400,
fontSize: 14,
overflow: TextOverflow.ellipsis,
),
),
],
),
);
}).toList(),
],
),
],
),
);
}
// ===============================
// The remaining helper cards (no changes)
// ===============================
Widget _buildFixIssuesCard() => // same as before
Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Color(0xFFD7F0FF),
borderRadius: BorderRadius.circular(16),
border: Border.all(width: 1.5, color: AppColors.buttonColor),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
Text('Facing Issues?',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: AppColors.amountText)),
Text(
'Raise a ticket to resolve your issues.',
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w400,
color: AppColors.subtitleText),
),
],
),
SvgPicture.asset("assets/svg/requirements.svg",
height: 22, width: 22),
],
),
);
Widget _buildGetInTouchCard() => // same as before
Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
border: Border.all(color: Colors.grey.shade200),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
),
child: const Row(
children: [
Icon(Icons.support_agent,
color: Color(0xFF1487C9), size: 24),
SizedBox(width: 16),
Expanded(
child: Text(
'Get in touch With Us',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Colors.black87),
),
),
],
),
);
}
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