454 lines
15 KiB
Dart
454 lines
15 KiB
Dart
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'package:go_router/go_router.dart';
|
|
import 'package:google_fonts/google_fonts.dart';
|
|
import 'package:nhance_partner/presentation/providers/userRoleProvider.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
|
|
import '../../../../core/routing/routes.dart';
|
|
import '../../../../core/services/api_service.dart';
|
|
import '../../../providers/manager_provider.dart';
|
|
import 'enquiry_tab.dart';
|
|
import 'quotation_tab.dart';
|
|
import 'policy_tab.dart';
|
|
import '../../../layouts/main_layout.dart';
|
|
|
|
class TabEnquiryList extends ConsumerStatefulWidget {
|
|
String? id;
|
|
TabEnquiryList({super.key, this.id});
|
|
@override
|
|
ConsumerState<TabEnquiryList> createState() => TabEnquiryListState();
|
|
}
|
|
|
|
class TabEnquiryListState extends ConsumerState<TabEnquiryList> {
|
|
int? expandedIndex;
|
|
int selectedIndex = 0;
|
|
List<TabItem> tabs = [];
|
|
Map<String, dynamic>? enquiryData;
|
|
bool isLoading = true;
|
|
late ApiService apiService;
|
|
ScrollController _scrollController = ScrollController();
|
|
dynamic role;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
apiService = ApiService();
|
|
|
|
if (kIsWeb) {
|
|
Future.microtask(() => _restoreId(ref));
|
|
}
|
|
|
|
Future.microtask(() {
|
|
final id = ref.read(enquiryIdProvider);
|
|
role = ref.read(userRoleProvider);
|
|
print("IntialID - $id");
|
|
if (id != null) {
|
|
_loadData(id);
|
|
} else {
|
|
setState(() => isLoading = false);
|
|
tabs = [
|
|
TabItem("Enquiry", EnquiryTab()),
|
|
TabItem("Proposal", QuotationTab()),
|
|
TabItem("Policy", PolicyTab()),
|
|
];
|
|
}
|
|
});
|
|
// fetch immediately
|
|
}
|
|
|
|
Future<void> _restoreId(WidgetRef ref) async {
|
|
final prefs = await SharedPreferences.getInstance();
|
|
|
|
final String? savedId = prefs.getString(
|
|
'enqAgentDataId',
|
|
); // already a string
|
|
if (savedId != null) {
|
|
ref.read(enquiryIdProvider.notifier).state = savedId;
|
|
print("savedenqStaffDataId ID restored: $savedId");
|
|
|
|
_loadData(savedId);
|
|
}
|
|
}
|
|
|
|
Future<void> _loadData1(id) async {
|
|
print('Load Data');
|
|
setState(() => isLoading = true);
|
|
final response = await apiService.findEnqQuotePolicyView(id);
|
|
setState(() {
|
|
enquiryData = response["data"];
|
|
print('Load Data - $enquiryData');
|
|
isLoading = false;
|
|
tabs = [
|
|
TabItem(
|
|
"Enquiry",
|
|
EnquiryTab(data: enquiryData?["enquiry"], id: id ?? ""),
|
|
),
|
|
TabItem(
|
|
"Proposal",
|
|
QuotationTab(
|
|
data:
|
|
(enquiryData?["quotations"] as List<dynamic>?)
|
|
?.map((e) => Map<String, dynamic>.from(e as Map))
|
|
.toList() ??
|
|
[],
|
|
id: id ?? "",
|
|
),
|
|
// QuotationTab(
|
|
// data: (enquiryData?["quotations"] as List<dynamic>?)!
|
|
// .map((e) => Map<String, dynamic>.from(e as Map))
|
|
// .toList(),
|
|
// id: id ?? "",
|
|
// ),
|
|
),
|
|
TabItem(
|
|
"Policy",
|
|
|
|
PolicyTab(
|
|
data:
|
|
(enquiryData?["policies"] as List<dynamic>?)
|
|
?.map((e) => Map<String, dynamic>.from(e as Map))
|
|
.toList() ??
|
|
[], // fallback to empty list
|
|
id: id ?? "",
|
|
),
|
|
// PolicyTab(
|
|
// data: (enquiryData?["policies"] as List<dynamic>?)!
|
|
// .map((e) => Map<String, dynamic>.from(e as Map))
|
|
// .toList(),
|
|
// id: id ?? "",
|
|
// ),
|
|
),
|
|
|
|
// TabItem("Proposal", QuotationTab(data: enquiryData?["quotations"])),
|
|
// TabItem("Policy", PolicyTab()),
|
|
];
|
|
});
|
|
}
|
|
|
|
Future<void> _loadData(id, {int? tabIndex}) async {
|
|
print('loadQuotationTab 2');
|
|
setState(() => isLoading = true);
|
|
final response = await apiService.findEnqQuotePolicyView(id);
|
|
|
|
setState(() {
|
|
enquiryData = response["data"];
|
|
isLoading = false;
|
|
tabs = [
|
|
TabItem(
|
|
"Enquiry",
|
|
EnquiryTab(data: enquiryData?["enquiry"], id: id ?? ""),
|
|
),
|
|
TabItem(
|
|
"Proposal",
|
|
QuotationTab(
|
|
data:
|
|
(enquiryData?["quotations"] as List<dynamic>?)
|
|
?.map((e) => Map<String, dynamic>.from(e as Map))
|
|
.toList() ??
|
|
[],
|
|
id: id ?? "",
|
|
onRefresh: () async {
|
|
await _loadData(id); // refresh parent data
|
|
},
|
|
),
|
|
),
|
|
TabItem(
|
|
"Policy",
|
|
PolicyTab(
|
|
data: enquiryData?["policies"] is List
|
|
? List<Map<String, dynamic>>.from(
|
|
(enquiryData?["policies"] as List).map(
|
|
(e) => Map<String, dynamic>.from(e as Map),
|
|
),
|
|
)
|
|
: enquiryData?["policies"] is Map
|
|
? [Map<String, dynamic>.from(enquiryData?["policies"] as Map)]
|
|
: [],
|
|
id: id ?? "",
|
|
),
|
|
),
|
|
|
|
// TabItem(
|
|
// "Policy",
|
|
// PolicyTab(
|
|
// data:
|
|
// (enquiryData?["policies"] as List<dynamic>?)
|
|
// ?.map((e) => Map<String, dynamic>.from(e as Map))
|
|
// .toList() ??
|
|
// [],
|
|
// id: id ?? "",
|
|
// ),
|
|
// ),
|
|
];
|
|
|
|
if (tabIndex != null) {
|
|
selectedIndex = tabIndex;
|
|
expandedIndex = tabIndex;
|
|
}
|
|
});
|
|
}
|
|
|
|
/// Public method to load data and select a tab
|
|
Future<void> loadQuotationTab(String id) async {
|
|
print('loadQuotationTab 1');
|
|
await _loadData(id, tabIndex: 2); // now _loadData will set the selected tab
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final bool isMobile = MediaQuery.of(context).size.width < 600;
|
|
final id = ref.watch(enquiryIdProvider);
|
|
|
|
print("TABUPDID- $id");
|
|
|
|
// // When provider changes, re-fetch
|
|
// ref.listen<String?>(enquiryIdProvider, (prev, next) {
|
|
// if (prev != next) {
|
|
// _loadData(id);
|
|
// }
|
|
// });
|
|
|
|
print('ENQID: $id');
|
|
|
|
return MainLayout(
|
|
title: "Enquiry",
|
|
|
|
body: SafeArea(
|
|
child: SelectionArea(
|
|
child: isLoading
|
|
? const Center(child: CircularProgressIndicator())
|
|
: isMobile
|
|
? SingleChildScrollView(
|
|
child: Column(
|
|
children: [
|
|
Padding(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 1,
|
|
vertical: 1,
|
|
),
|
|
child: GestureDetector(
|
|
onTap: () {
|
|
// context.go(AppRoutes.dashboard);
|
|
|
|
if (role == 'handler') {
|
|
ref.read(enquiryIdProvider.notifier).state = null;
|
|
context.go(AppRoutes.enquiryHandlerLst);
|
|
} else if (role == 'manager') {
|
|
ref.read(enquiryIdProvider.notifier).state = null;
|
|
context.go(AppRoutes.enquiryForStaff);
|
|
} else {
|
|
ref.read(enquiryIdProvider.notifier).state = null;
|
|
context.go(AppRoutes.enquiryLst);
|
|
}
|
|
},
|
|
child: Row(
|
|
children: [
|
|
const Icon(
|
|
Icons.arrow_left_sharp,
|
|
size: 20,
|
|
color: Color(0xFF425B5B),
|
|
),
|
|
// const SizedBox(width: 8),
|
|
Text(
|
|
"Enquiry ",
|
|
style: GoogleFonts.inter(
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.w600,
|
|
color: Colors.black,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
_buildMobileTabs(
|
|
shrinkWrap: true,
|
|
physics: NeverScrollableScrollPhysics(),
|
|
),
|
|
],
|
|
),
|
|
)
|
|
: _buildDesktopTabs(),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildMobileTabs({
|
|
required bool shrinkWrap,
|
|
required ScrollPhysics physics,
|
|
}) {
|
|
return ListView.builder(
|
|
shrinkWrap: shrinkWrap,
|
|
physics: physics,
|
|
itemCount: tabs.length,
|
|
padding: const EdgeInsets.all(16),
|
|
itemBuilder: (context, index) {
|
|
final isExpanded = expandedIndex == index;
|
|
|
|
return Card(
|
|
margin: const EdgeInsets.only(bottom: 12),
|
|
color: isExpanded ? const Color(0xFFEDF6F5) : const Color(0xFF425B5B),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
side: const BorderSide(color: Color(0xFF425B5B), width: 1),
|
|
),
|
|
child: Theme(
|
|
// ✅ removes top & bottom dividers inside ExpansionTile
|
|
data: Theme.of(context).copyWith(dividerColor: Colors.transparent),
|
|
child: ExpansionTile(
|
|
key: ValueKey(index),
|
|
collapsedIconColor: isExpanded ? Colors.black : Colors.white,
|
|
iconColor: isExpanded ? Colors.black : Colors.white,
|
|
initiallyExpanded: isExpanded,
|
|
tilePadding: const EdgeInsets.symmetric(horizontal: 16.0),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
collapsedShape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
onExpansionChanged: (expanded) {
|
|
setState(() {
|
|
expandedIndex = expanded ? index : null;
|
|
if (expanded) {
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
RenderBox box = context.findRenderObject() as RenderBox;
|
|
double yPos = box.localToGlobal(Offset.zero).dy;
|
|
_scrollController.animateTo(
|
|
_scrollController.offset + yPos - 100,
|
|
duration: const Duration(milliseconds: 300),
|
|
curve: Curves.easeInOut,
|
|
);
|
|
});
|
|
}
|
|
});
|
|
},
|
|
title: Text(
|
|
tabs[index].title,
|
|
style: GoogleFonts.inter(
|
|
fontSize: 14,
|
|
color: isExpanded ? Colors.black : Colors.white,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
children: [
|
|
Container(
|
|
// padding: const EdgeInsets.all(12),
|
|
child: tabs[index].widget,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
Widget _buildDesktopTabs() {
|
|
return Column(
|
|
children: [
|
|
Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 2, vertical: 2),
|
|
child: GestureDetector(
|
|
onTap: () {
|
|
// context.go(AppRoutes.dashboard);
|
|
// ref.read(enquiryIdProvider.notifier).state = null;
|
|
// context.go(AppRoutes.enquiryLst);
|
|
|
|
if (role == 'handler') {
|
|
ref.read(enquiryIdProvider.notifier).state = null;
|
|
context.go(AppRoutes.enquiryHandlerLst);
|
|
} else {
|
|
ref.read(enquiryIdProvider.notifier).state = null;
|
|
context.go(AppRoutes.enquiryLst);
|
|
}
|
|
},
|
|
child: Row(
|
|
children: [
|
|
Tooltip(
|
|
message: 'Back',
|
|
child: IconButton(
|
|
icon: const Icon(
|
|
Icons.arrow_left_sharp,
|
|
size: 25,
|
|
color: Color(0xFF425B5B),
|
|
),
|
|
onPressed: () {
|
|
if (role == 'handler') {
|
|
ref.read(enquiryIdProvider.notifier).state = null;
|
|
context.go(AppRoutes.enquiryHandlerLst);
|
|
} else if (role == 'manager') {
|
|
ref.read(enquiryIdProvider.notifier).state = null;
|
|
context.go(AppRoutes.enquiryForStaff);
|
|
} else {
|
|
ref.read(enquiryIdProvider.notifier).state = null;
|
|
context.go(AppRoutes.enquiryLst);
|
|
}
|
|
|
|
// ref.read(enquiryIdProvider.notifier).state = null;
|
|
// context.go(AppRoutes.enquiryLst);
|
|
},
|
|
splashRadius: 28,
|
|
hoverColor: Colors.black12,
|
|
padding: const EdgeInsets.all(8),
|
|
constraints: const BoxConstraints(),
|
|
),
|
|
),
|
|
const SizedBox(width: 8),
|
|
Text(
|
|
"Enquiry",
|
|
style: GoogleFonts.inter(
|
|
fontSize: 18,
|
|
fontWeight: FontWeight.w700,
|
|
color: Colors.black87,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
Row(
|
|
children: List.generate(tabs.length, (index) {
|
|
final isSelected = selectedIndex == index;
|
|
return Padding(
|
|
padding: const EdgeInsets.only(left: 10.0),
|
|
child: ElevatedButton(
|
|
style: ElevatedButton.styleFrom(
|
|
elevation: 2,
|
|
backgroundColor: isSelected
|
|
? const Color(0xFF425B5B)
|
|
: const Color(0xFFEDFFFC),
|
|
foregroundColor: isSelected ? Colors.white : Colors.black87,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(6),
|
|
),
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 24,
|
|
vertical: 12,
|
|
),
|
|
),
|
|
onPressed: () {
|
|
setState(() => selectedIndex = index);
|
|
},
|
|
child: Text(tabs[index].title),
|
|
),
|
|
);
|
|
}),
|
|
),
|
|
|
|
SizedBox(height: 10),
|
|
Expanded(child: tabs[selectedIndex].widget),
|
|
],
|
|
);
|
|
}
|
|
}
|
|
|
|
class TabItem {
|
|
final String title;
|
|
final Widget widget;
|
|
TabItem(this.title, this.widget);
|
|
}
|