426 lines
17 KiB
Dart
426 lines
17 KiB
Dart
import 'dart:convert';
|
|
import 'dart:core';
|
|
import 'package:frontend/data/models/plan.dart';
|
|
import 'package:go_router/go_router.dart';
|
|
import 'package:http/http.dart' as http;
|
|
import 'package:flutter/material.dart';
|
|
import 'package:frontend/config/apiUrl.dart';
|
|
import 'package:responsive_builder/responsive_builder.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
|
|
import '../../routes/custom_appBar.dart';
|
|
import '../../routes/custom_drawer.dart';
|
|
|
|
|
|
class ListPlans extends StatefulWidget{
|
|
const ListPlans({super.key});
|
|
|
|
@override
|
|
_ListPlansState createState() => _ListPlansState();
|
|
}
|
|
|
|
|
|
class _ListPlansState extends State<ListPlans>{
|
|
|
|
late Future<List<Plan>> futurePlans;
|
|
String? userId;
|
|
String? token;
|
|
|
|
@override
|
|
void initState(){
|
|
super.initState();
|
|
getToken();
|
|
initializeData();
|
|
|
|
|
|
// futurePlans = fetchPlans();
|
|
|
|
}
|
|
|
|
Future<void> initializeData ()async{
|
|
token = await getToken();
|
|
userId = await getUserId();
|
|
|
|
if(token == null || userId == null){
|
|
print("Token or USerId missing");
|
|
return;
|
|
}
|
|
else{
|
|
setState(() {
|
|
futurePlans = fetchPlans();
|
|
});
|
|
}
|
|
}
|
|
|
|
|
|
|
|
Future<String?> getUserId() async {
|
|
final prefs = await SharedPreferences.getInstance();
|
|
final String? userDataString = prefs.getString('user_data');
|
|
|
|
if(userDataString != null){
|
|
try{
|
|
final Map<String,dynamic> userData = jsonDecode(userDataString);
|
|
return userData["user_id"]?.toString();
|
|
}catch(e){
|
|
return null;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
|
|
Future<String?> getToken() async {
|
|
final prefs = await SharedPreferences.getInstance();
|
|
return prefs.getString('auth_token');
|
|
|
|
}
|
|
|
|
|
|
// Fetch API Data
|
|
Future<List<Plan>> fetchPlans() async {
|
|
// final String apiUrldata = '$apiUrl/api/plans';
|
|
final String apiUrldata = '$apiUrl/api/plans?user_id=$userId';
|
|
|
|
// final token = await getToken();
|
|
|
|
|
|
if (token == null) {
|
|
throw Exception('Token not found. Please log in.');
|
|
}
|
|
|
|
final response = await http.get(
|
|
Uri.parse(apiUrldata),
|
|
headers: {
|
|
'Authorization': 'Bearer $token', // Add token here
|
|
'Content-Type': 'application/json',
|
|
},
|
|
);
|
|
|
|
if (response.statusCode == 200) {
|
|
final data = json.decode(response.body);
|
|
List<dynamic> plansJson = data['data'];
|
|
return plansJson.map((json) => Plan.fromJson(json)).toList();
|
|
} else {
|
|
throw Exception('Failed to load plans');
|
|
}
|
|
}
|
|
|
|
|
|
Future <Map<String,dynamic>> getViewPlan(String planId) async{
|
|
final String apiUrldata = '$apiUrl/api/plans/find/$planId';
|
|
print("API URL: $apiUrldata");
|
|
// final token = await getToken();
|
|
|
|
if (token == null) {
|
|
throw Exception('Token not found. Please log in.');
|
|
}
|
|
|
|
final response = await http.put(
|
|
Uri.parse(apiUrldata),
|
|
headers: {
|
|
'Authorization': 'Bearer $token', // Add token here
|
|
'Content-Type': 'application/json',
|
|
},
|
|
);
|
|
|
|
if (response.statusCode == 200) {
|
|
final Map<String,dynamic>? resData = json.decode(response.body);
|
|
|
|
return resData?["data"];
|
|
|
|
} else {
|
|
throw Exception('Failed to load plans');
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
void viewPlan(String planId, {bool isViewMode = false}) async{
|
|
try {
|
|
Map<String, dynamic> planData = await getViewPlan(planId);
|
|
print("ViewAAA - $planData");
|
|
|
|
context.go('/createPlan',extra: {'planData': planData, 'isViewMode': isViewMode} );
|
|
} catch (e) {
|
|
print("Error fetching plan: $e");
|
|
}
|
|
}
|
|
|
|
|
|
Widget build(BuildContext context) {
|
|
return ResponsiveBuilder(builder: (context, sizingInfo) {
|
|
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
|
|
|
|
return Scaffold(
|
|
backgroundColor: Colors.white,
|
|
appBar: isDesktop ? null : const CustomAppBar(title: 'Home'),
|
|
drawer: isDesktop ? null : CustomDrawer(isDesktop: false),
|
|
body: Row(
|
|
children: [
|
|
if (isDesktop) CustomDrawer(isDesktop: true),
|
|
Expanded(child: buildTableLayout(isDesktop))
|
|
],
|
|
),
|
|
);
|
|
});
|
|
}
|
|
|
|
Widget buildTableLayout(isDesktop) {
|
|
return Padding(
|
|
padding: const EdgeInsets.all(16.0),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: [
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Row(
|
|
children: [
|
|
const Text('Plans List',
|
|
style:
|
|
TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
|
|
IconButton(
|
|
icon: const Icon(Icons.keyboard_arrow_down),
|
|
onPressed: () {},
|
|
),
|
|
],
|
|
),
|
|
ElevatedButton(
|
|
style: ElevatedButton.styleFrom(
|
|
foregroundColor: Colors.white,
|
|
backgroundColor: Colors.blueAccent),
|
|
onPressed: () {
|
|
context.go('/createPlan');
|
|
if (!isDesktop) Navigator.pop(context);
|
|
},
|
|
child: Row(
|
|
children: [
|
|
Icon(Icons.add_circle,color: Colors.white,),
|
|
SizedBox(width: 5,),
|
|
Text('NewPlan'),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 10),
|
|
FutureBuilder<List<Plan>>(
|
|
future: futurePlans, // Use the futurePlans variable
|
|
builder: (context, snapshot) {
|
|
if (snapshot.connectionState == ConnectionState.waiting) {
|
|
return const Center(child: CircularProgressIndicator());
|
|
} else if (snapshot.hasError) {
|
|
return Center(child: Text("Error: ${snapshot.error}"));
|
|
} else if (!snapshot.hasData || snapshot.data!.isEmpty) {
|
|
return const Center(child: Text("No plans available"));
|
|
}
|
|
|
|
List<Plan> plans = snapshot.data!; // Extract the list of plans
|
|
|
|
|
|
// Ensure planId is sorted in descending order
|
|
plans.sort((a, b) => int.parse(b.planId.toString()).compareTo(int.parse(a.planId.toString())));
|
|
|
|
|
|
|
|
// return ResponsiveBuilder(
|
|
// builder: (context, sizingInfo) {
|
|
// bool isTabletOrDesktop = sizingInfo.isTablet || sizingInfo.isDesktop;
|
|
//
|
|
// return SingleChildScrollView(
|
|
// scrollDirection: Axis.horizontal,
|
|
// child: Container(
|
|
// color: Colors.grey,
|
|
// child: SizedBox(
|
|
// width: MediaQuery.of(context).size.width ,
|
|
// child: SingleChildScrollView(
|
|
// scrollDirection: Axis.vertical,
|
|
// // scrollDirection: isTabletOrDesktop ? Axis.vertical : Axis.horizontal,
|
|
//
|
|
// // constraints: isTabletOrDesktop
|
|
// // ? const BoxConstraints(maxWidth: double.infinity)
|
|
// // : BoxConstraints.tightFor(width: 600),
|
|
//
|
|
//
|
|
// child: DataTable(
|
|
// // columnSpacing: 50.0,
|
|
// dividerThickness: 0.5, // Reduce the thickness of row dividers
|
|
// border: TableBorder(
|
|
// horizontalInside: BorderSide(width: 0.5, color: Colors.grey.shade200), // Reduce horizontal line thickness
|
|
// ),
|
|
// columns: const [
|
|
// DataColumn(label: Text('Plan ID', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
|
|
// DataColumn(label: Text('Trip Title', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
|
|
// DataColumn(label: Text('Trip Type', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
|
|
// DataColumn(label: Text('Cost Center', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
|
|
// // DataColumn(label: Text('Functional Department', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
|
|
// // DataColumn(label: Text('Purpose Of Travel', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
|
|
// // DataColumn(label: Text('Description', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
|
|
// //
|
|
// DataColumn(label: Text('Is Billable', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
|
|
// DataColumn(label: Text('Status', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
|
|
// DataColumn(label: Text('Actions', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
|
|
// ],
|
|
// rows: plans.map((plan) {
|
|
// return DataRow(cells: [
|
|
// DataCell(Text(plan.planId)),
|
|
// // DataCell(Text(plan.tripTitle)),
|
|
// DataCell(Row(
|
|
// children: [
|
|
// Flexible(
|
|
// child: Text(
|
|
// plan.tripTitle,
|
|
// softWrap: true,
|
|
// overflow: TextOverflow.ellipsis, // Adds "..." if text is too long
|
|
// ),
|
|
// ),
|
|
// ],
|
|
// )),
|
|
//
|
|
//
|
|
// DataCell(Text(plan.tripType)),
|
|
// DataCell(Text(plan.costCenter)),
|
|
// // DataCell(Text(plan.functionalDepartment)),
|
|
// // DataCell(Text(plan.purposeOfTravel)),
|
|
// // DataCell(Text(plan.description)),
|
|
// //
|
|
// DataCell(Text(plan.isBillable)),
|
|
// DataCell(Text(plan.status)),
|
|
// DataCell(
|
|
// TextButton(
|
|
// onPressed: () {
|
|
// viewPlan(plan.planId);
|
|
// print("View button clicked for ${plan.planId}");
|
|
// },
|
|
// child: const Text('View',
|
|
// style: TextStyle(color: Colors.blueAccent)),
|
|
// ),
|
|
// ),
|
|
// ]);
|
|
// }).toList(),
|
|
// ),
|
|
//
|
|
//
|
|
// ),
|
|
// ),
|
|
// ),
|
|
// );
|
|
//
|
|
// },
|
|
// );
|
|
|
|
return Expanded(
|
|
child: SingleChildScrollView(
|
|
// scrollDirection: Axis.horizontal, // Outer wrapper for horizontal scrolling
|
|
scrollDirection: Axis.vertical,
|
|
child: SizedBox(
|
|
width: MediaQuery.of(context).size.width * 1.5,
|
|
// width: MediaQuery.of(context).size.width , // Ensure table is wider than screen
|
|
// width: double.infinity , // Ensure table is wider than screen
|
|
|
|
child: SingleChildScrollView(
|
|
// scrollDirection: Axis.vertical, // Inner wrapper for vertical scrolling
|
|
scrollDirection: Axis.horizontal, // Inner wrapper for vertical scrolling
|
|
|
|
|
|
child: ConstrainedBox(
|
|
constraints: BoxConstraints(minWidth: 1300),
|
|
// width: MediaQuery.of(context).size.width ,
|
|
|
|
child: Container(
|
|
// color: Colors.amber,
|
|
child: DataTable(
|
|
columnSpacing: 50.0, // Adjust spacing between columns
|
|
dividerThickness: 0.5,
|
|
border: TableBorder(
|
|
horizontalInside: BorderSide(width: 0.5, color: Colors.grey.shade200),
|
|
),
|
|
columns: const [
|
|
DataColumn(label: Text('Plan ID', style: TextStyle(fontWeight: FontWeight.bold))),
|
|
DataColumn(label: Text('Trip Title', style: TextStyle(fontWeight: FontWeight.bold))),
|
|
DataColumn(label: Text('Trip Type', style: TextStyle(fontWeight: FontWeight.bold))),
|
|
DataColumn(label: Text('Cost Center', style: TextStyle(fontWeight: FontWeight.bold))),
|
|
DataColumn(label: Text('Is Billable', style: TextStyle(fontWeight: FontWeight.bold))),
|
|
DataColumn(label: Text('Status', style: TextStyle(fontWeight: FontWeight.bold))),
|
|
DataColumn(label: Text('Actions', style: TextStyle(fontWeight: FontWeight.bold))),
|
|
],
|
|
|
|
|
|
|
|
rows: plans.map((plan) {
|
|
return DataRow(cells: [
|
|
DataCell(Text(plan.planId)),
|
|
DataCell(Text(plan.tripTitle, softWrap: true, overflow: TextOverflow.ellipsis)),
|
|
DataCell(Text(plan.tripType)),
|
|
DataCell(Text(plan.costCenter)),
|
|
DataCell(Text(plan.isBillable)),
|
|
DataCell(
|
|
Container(
|
|
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 10), // Padding for better look
|
|
decoration: BoxDecoration(
|
|
color: plan.status == "Active" ? Colors.green.shade50 : Colors.grey.shade50, // Background color
|
|
borderRadius: BorderRadius.circular(10), // Rounded corners
|
|
),
|
|
child: Text(
|
|
plan.status,
|
|
style: TextStyle(
|
|
color: plan.status == "Active" ? Colors.green : Colors.grey, // Text color
|
|
fontWeight: FontWeight.bold, // Optional: Make text bold
|
|
),
|
|
),
|
|
),
|
|
),
|
|
DataCell(
|
|
Row(
|
|
children:[
|
|
|
|
IconButton(
|
|
icon: Icon(Icons.remove_red_eye, color: Colors.blue),
|
|
onPressed: () {
|
|
viewPlan(plan.planId, isViewMode: true);
|
|
|
|
},
|
|
),
|
|
IconButton(
|
|
icon: Icon(Icons.edit, color: Colors.green),
|
|
onPressed: () {
|
|
viewPlan(plan.planId, isViewMode: false);
|
|
},
|
|
),
|
|
// IconButton(
|
|
// icon: Icon(Icons.delete, color: Colors.red),
|
|
// onPressed: () {
|
|
// deletePlan(plan.planId);
|
|
// },
|
|
// ),
|
|
|
|
|
|
]
|
|
)
|
|
|
|
),
|
|
]);
|
|
}).toList(),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
|
|
|
|
|
|
},
|
|
),
|
|
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|