added team member list
This commit is contained in:
parent
6cd9fbe57b
commit
03082aeea9
@ -1,6 +1,7 @@
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:on_field_work/helpers/services/api_service.dart';
|
import 'package:on_field_work/helpers/services/api_service.dart';
|
||||||
import 'package:on_field_work/model/infra_project/infra_project_details.dart';
|
import 'package:on_field_work/model/infra_project/infra_project_details.dart';
|
||||||
|
import 'package:on_field_work/model/infra_project/infra_team_list_model.dart';
|
||||||
|
|
||||||
class InfraProjectDetailsController extends GetxController {
|
class InfraProjectDetailsController extends GetxController {
|
||||||
final String projectId;
|
final String projectId;
|
||||||
@ -9,25 +10,39 @@ class InfraProjectDetailsController extends GetxController {
|
|||||||
|
|
||||||
var isLoading = true.obs;
|
var isLoading = true.obs;
|
||||||
var projectDetails = Rxn<ProjectData>();
|
var projectDetails = Rxn<ProjectData>();
|
||||||
|
var teamList = <ProjectAllocation>[].obs;
|
||||||
|
var teamLoading = true.obs;
|
||||||
var errorMessage = ''.obs;
|
var errorMessage = ''.obs;
|
||||||
|
var teamErrorMessage = ''.obs;
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
super.onInit();
|
super.onInit();
|
||||||
fetchProjectDetails();
|
fetchProjectDetails();
|
||||||
|
fetchProjectTeamList();
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, List<ProjectAllocation>> get groupedTeamByRole {
|
||||||
|
final Map<String, List<ProjectAllocation>> map = {};
|
||||||
|
for (final member in teamList) {
|
||||||
|
map.putIfAbsent(member.jobRoleId, () => []).add(member);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> fetchProjectDetails() async {
|
Future<void> fetchProjectDetails() async {
|
||||||
try {
|
try {
|
||||||
isLoading.value = true;
|
isLoading.value = true;
|
||||||
final response = await ApiService.getInfraProjectDetails(projectId: projectId);
|
final response =
|
||||||
|
await ApiService.getInfraProjectDetails(projectId: projectId);
|
||||||
|
|
||||||
if (response != null && response.success == true && response.data != null) {
|
if (response != null &&
|
||||||
|
response.success == true &&
|
||||||
|
response.data != null) {
|
||||||
projectDetails.value = response.data;
|
projectDetails.value = response.data;
|
||||||
isLoading.value = false;
|
errorMessage.value = '';
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
errorMessage.value = response?.message ?? "Failed to load project details";
|
errorMessage.value =
|
||||||
|
response?.message ?? "Failed to load project details";
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
errorMessage.value = "Error fetching project details: $e";
|
errorMessage.value = "Error fetching project details: $e";
|
||||||
@ -35,4 +50,28 @@ class InfraProjectDetailsController extends GetxController {
|
|||||||
isLoading.value = false;
|
isLoading.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> fetchProjectTeamList() async {
|
||||||
|
try {
|
||||||
|
teamLoading.value = true;
|
||||||
|
teamErrorMessage.value = '';
|
||||||
|
|
||||||
|
final response = await ApiService.getInfraProjectTeamListApi(
|
||||||
|
projectId: projectId,
|
||||||
|
includeInactive: false,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response?.success == true && response!.data.isNotEmpty) {
|
||||||
|
teamList.assignAll(response.data);
|
||||||
|
} else {
|
||||||
|
teamList.clear();
|
||||||
|
teamErrorMessage.value = response?.message ?? "No team members found.";
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
teamList.clear();
|
||||||
|
teamErrorMessage.value = "Failed to load team members";
|
||||||
|
} finally {
|
||||||
|
teamLoading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -167,4 +167,6 @@ class ApiEndpoints {
|
|||||||
// Infra Project Module API Endpoints
|
// Infra Project Module API Endpoints
|
||||||
static const String getInfraProjectsList = "/project/list";
|
static const String getInfraProjectsList = "/project/list";
|
||||||
static const String getInfraProjectDetail = "/project/details";
|
static const String getInfraProjectDetail = "/project/details";
|
||||||
|
static const String getInfraProjectTeamList = "/project/allocation";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -51,6 +51,7 @@ import 'package:on_field_work/model/infra_project/infra_project_list.dart';
|
|||||||
import 'package:on_field_work/model/infra_project/infra_project_details.dart';
|
import 'package:on_field_work/model/infra_project/infra_project_details.dart';
|
||||||
import 'package:on_field_work/model/dashboard/collection_overview_model.dart';
|
import 'package:on_field_work/model/dashboard/collection_overview_model.dart';
|
||||||
import 'package:on_field_work/model/dashboard/purchase_invoice_model.dart';
|
import 'package:on_field_work/model/dashboard/purchase_invoice_model.dart';
|
||||||
|
import 'package:on_field_work/model/infra_project/infra_team_list_model.dart';
|
||||||
|
|
||||||
class ApiService {
|
class ApiService {
|
||||||
static const bool enableLogs = true;
|
static const bool enableLogs = true;
|
||||||
@ -2007,6 +2008,43 @@ class ApiService {
|
|||||||
label: "Comment Task", returnFullResponse: true);
|
label: "Comment Task", returnFullResponse: true);
|
||||||
return parsed != null && parsed['success'] == true;
|
return parsed != null && parsed['success'] == true;
|
||||||
}
|
}
|
||||||
|
static Future<ProjectAllocationResponse?> getInfraProjectTeamListApi({
|
||||||
|
required String projectId,
|
||||||
|
String? serviceId,
|
||||||
|
bool includeInactive = false,
|
||||||
|
}) async {
|
||||||
|
if (projectId.isEmpty) {
|
||||||
|
_log("projectId must not be empty for getInfraProjectTeamListApi",
|
||||||
|
level: LogLevel.error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final endpoint = "${ApiEndpoints.getInfraProjectTeamList}/$projectId";
|
||||||
|
|
||||||
|
final queryParams = <String, String>{
|
||||||
|
'includeInactive': includeInactive.toString(),
|
||||||
|
};
|
||||||
|
if (serviceId != null && serviceId.isNotEmpty) {
|
||||||
|
queryParams['serviceId'] = serviceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
final response = await _safeApiCall(
|
||||||
|
endpoint,
|
||||||
|
method: 'GET',
|
||||||
|
queryParams: queryParams,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response == null) return null;
|
||||||
|
|
||||||
|
final parsedJson = _parseAndDecryptResponse(
|
||||||
|
response,
|
||||||
|
label: "InfraProjectTeamList",
|
||||||
|
returnFullResponse: true,
|
||||||
|
);
|
||||||
|
if (parsedJson == null || parsedJson is! Map<String, dynamic>) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return ProjectAllocationResponse.fromJson(parsedJson);
|
||||||
|
}
|
||||||
|
|
||||||
static Future<Map<String, dynamic>?> getInfraDetails(String projectId,
|
static Future<Map<String, dynamic>?> getInfraDetails(String projectId,
|
||||||
{String? serviceId}) async {
|
{String? serviceId}) async {
|
||||||
|
|||||||
119
lib/model/infra_project/infra_team_list_model.dart
Normal file
119
lib/model/infra_project/infra_team_list_model.dart
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
|
||||||
|
class ProjectAllocationResponse {
|
||||||
|
final bool success;
|
||||||
|
final String message;
|
||||||
|
final List<ProjectAllocation> data;
|
||||||
|
final int statusCode;
|
||||||
|
final String timestamp;
|
||||||
|
|
||||||
|
ProjectAllocationResponse({
|
||||||
|
required this.success,
|
||||||
|
required this.message,
|
||||||
|
required this.data,
|
||||||
|
required this.statusCode,
|
||||||
|
required this.timestamp,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory ProjectAllocationResponse.fromJson(Map<String, dynamic> json) {
|
||||||
|
final List<dynamic>? rawData = json['data'] as List<dynamic>?;
|
||||||
|
|
||||||
|
final List<ProjectAllocation> allocations = rawData
|
||||||
|
?.map((item) => ProjectAllocation.fromJson(item as Map<String, dynamic>))
|
||||||
|
.toList()
|
||||||
|
?? [];
|
||||||
|
|
||||||
|
return ProjectAllocationResponse(
|
||||||
|
success: json['success'] as bool? ?? false,
|
||||||
|
message: json['message'] as String? ?? 'An unknown API error occurred.',
|
||||||
|
data: allocations,
|
||||||
|
statusCode: json['statusCode'] as int? ?? 0,
|
||||||
|
timestamp: json['timestamp'] as String? ?? '',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts the [ProjectAllocationResponse] object back to a JSON map.
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
'success': success,
|
||||||
|
'message': message,
|
||||||
|
'data': data.map((e) => e.toJson()).toList(),
|
||||||
|
'statusCode': statusCode,
|
||||||
|
'timestamp': timestamp,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Allocation Detail Class ---
|
||||||
|
|
||||||
|
class ProjectAllocation {
|
||||||
|
final String id;
|
||||||
|
final String employeeId;
|
||||||
|
final String projectId;
|
||||||
|
final String allocationDate;
|
||||||
|
final bool isActive;
|
||||||
|
final String firstName;
|
||||||
|
final String lastName;
|
||||||
|
final String middleName;
|
||||||
|
final String organizationId;
|
||||||
|
final String organizationName;
|
||||||
|
final String serviceId;
|
||||||
|
final String serviceName;
|
||||||
|
final String jobRoleId;
|
||||||
|
final String jobRoleName;
|
||||||
|
|
||||||
|
ProjectAllocation({
|
||||||
|
required this.id,
|
||||||
|
required this.employeeId,
|
||||||
|
required this.projectId,
|
||||||
|
required this.allocationDate,
|
||||||
|
required this.isActive,
|
||||||
|
required this.firstName,
|
||||||
|
required this.lastName,
|
||||||
|
required this.middleName,
|
||||||
|
required this.organizationId,
|
||||||
|
required this.organizationName,
|
||||||
|
required this.serviceId,
|
||||||
|
required this.serviceName,
|
||||||
|
required this.jobRoleId,
|
||||||
|
required this.jobRoleName
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
factory ProjectAllocation.fromJson(Map<String, dynamic> json) {
|
||||||
|
return ProjectAllocation(
|
||||||
|
id: json['id'] as String? ?? '',
|
||||||
|
employeeId: json['employeeId'] as String? ?? '',
|
||||||
|
projectId: json['projectId'] as String? ?? '',
|
||||||
|
allocationDate: json['allocationDate'] as String? ?? '',
|
||||||
|
isActive: json['isActive'] as bool? ?? false,
|
||||||
|
firstName: json['firstName'] as String? ?? '',
|
||||||
|
lastName: json['lastName'] as String? ?? '',
|
||||||
|
middleName: json['middleName'] as String? ?? '',
|
||||||
|
organizationId: json['organizationId'] as String? ?? '',
|
||||||
|
organizationName: json['organizationName'] as String? ?? '',
|
||||||
|
serviceId: json['serviceId'] as String? ?? '',
|
||||||
|
serviceName: json['serviceName'] as String? ?? '',
|
||||||
|
jobRoleId: json['jobRoleId'] as String? ?? '',
|
||||||
|
jobRoleName: json['jobRoleName'] as String? ?? '',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
'id': id,
|
||||||
|
'employeeId': employeeId,
|
||||||
|
'projectId': projectId,
|
||||||
|
'allocationDate': allocationDate,
|
||||||
|
'isActive': isActive,
|
||||||
|
'firstName': firstName,
|
||||||
|
'lastName': lastName,
|
||||||
|
'middleName': middleName,
|
||||||
|
'organizationId': organizationId,
|
||||||
|
'organizationName': organizationName,
|
||||||
|
'serviceId': serviceId,
|
||||||
|
'serviceName': serviceName,
|
||||||
|
'jobRoleId': jobRoleId,
|
||||||
|
'jobRoleName': jobRoleName
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -10,11 +10,14 @@ import 'package:on_field_work/helpers/utils/launcher_utils.dart';
|
|||||||
import 'package:on_field_work/helpers/widgets/my_spacing.dart';
|
import 'package:on_field_work/helpers/widgets/my_spacing.dart';
|
||||||
import 'package:on_field_work/helpers/widgets/my_text.dart';
|
import 'package:on_field_work/helpers/widgets/my_text.dart';
|
||||||
import 'package:on_field_work/helpers/widgets/my_refresh_indicator.dart';
|
import 'package:on_field_work/helpers/widgets/my_refresh_indicator.dart';
|
||||||
|
import 'package:on_field_work/helpers/widgets/avatar.dart';
|
||||||
|
import 'package:on_field_work/view/employees/employee_profile_screen.dart';
|
||||||
|
|
||||||
import 'package:on_field_work/controller/dynamicMenu/dynamic_menu_controller.dart';
|
import 'package:on_field_work/controller/dynamicMenu/dynamic_menu_controller.dart';
|
||||||
import 'package:on_field_work/controller/infra_project/infra_project_screen_details_controller.dart';
|
import 'package:on_field_work/controller/infra_project/infra_project_screen_details_controller.dart';
|
||||||
import 'package:on_field_work/view/taskPlanning/daily_progress_report.dart';
|
import 'package:on_field_work/view/taskPlanning/daily_progress_report.dart';
|
||||||
import 'package:on_field_work/view/taskPlanning/daily_task_planning.dart';
|
import 'package:on_field_work/view/taskPlanning/daily_task_planning.dart';
|
||||||
|
import 'package:on_field_work/model/infra_project/infra_team_list_model.dart';
|
||||||
|
|
||||||
class InfraProjectDetailsScreen extends StatefulWidget {
|
class InfraProjectDetailsScreen extends StatefulWidget {
|
||||||
final String projectId;
|
final String projectId;
|
||||||
@ -36,17 +39,20 @@ class _InfraProjectDetailsScreenState extends State<InfraProjectDetailsScreen>
|
|||||||
late final TabController _tabController;
|
late final TabController _tabController;
|
||||||
final DynamicMenuController menuController =
|
final DynamicMenuController menuController =
|
||||||
Get.find<DynamicMenuController>();
|
Get.find<DynamicMenuController>();
|
||||||
|
late final InfraProjectDetailsController controller;
|
||||||
final List<_InfraTab> _tabs = [];
|
final List<_InfraTab> _tabs = [];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
controller =
|
||||||
|
Get.put(InfraProjectDetailsController(projectId: widget.projectId));
|
||||||
_prepareTabs();
|
_prepareTabs();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _prepareTabs() {
|
void _prepareTabs() {
|
||||||
// Profile tab is always added
|
|
||||||
_tabs.add(_InfraTab(name: "Profile", view: _buildProfileTab()));
|
_tabs.add(_InfraTab(name: "Profile", view: _buildProfileTab()));
|
||||||
|
_tabs.add(_InfraTab(name: "Team", view: _buildTeamTab()));
|
||||||
|
|
||||||
final allowedMenu = menuController.menuItems.where((m) => m.available);
|
final allowedMenu = menuController.menuItems.where((m) => m.available);
|
||||||
|
|
||||||
@ -77,10 +83,148 @@ class _InfraProjectDetailsScreenState extends State<InfraProjectDetailsScreen>
|
|||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildProfileTab() {
|
Widget _buildTeamTab() {
|
||||||
final controller =
|
return Obx(() {
|
||||||
Get.put(InfraProjectDetailsController(projectId: widget.projectId));
|
if (controller.teamLoading.value) {
|
||||||
|
return const Center(child: CircularProgressIndicator());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (controller.teamErrorMessage.isNotEmpty) {
|
||||||
|
return Center(
|
||||||
|
child: MyText.bodyMedium(controller.teamErrorMessage.value),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (controller.teamList.isEmpty) {
|
||||||
|
return const Center(
|
||||||
|
child: Text("No team members allocated to this project."),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final roleGroups = controller.groupedTeamByRole;
|
||||||
|
|
||||||
|
final sortedRoleEntries = roleGroups.entries.toList()
|
||||||
|
..sort((a, b) {
|
||||||
|
final aName = (a.value.isNotEmpty ? a.value.first.jobRoleName : '')
|
||||||
|
.toLowerCase();
|
||||||
|
final bName = (b.value.isNotEmpty ? b.value.first.jobRoleName : '')
|
||||||
|
.toLowerCase();
|
||||||
|
return aName.compareTo(bName);
|
||||||
|
});
|
||||||
|
|
||||||
|
return MyRefreshIndicator(
|
||||||
|
onRefresh: controller.fetchProjectTeamList,
|
||||||
|
backgroundColor: Colors.indigo,
|
||||||
|
color: Colors.white,
|
||||||
|
child: ListView.separated(
|
||||||
|
padding: const EdgeInsets.all(12),
|
||||||
|
itemCount: sortedRoleEntries.length,
|
||||||
|
separatorBuilder: (_, __) => const SizedBox(height: 12),
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final teamMembers = sortedRoleEntries[index].value;
|
||||||
|
return _buildRoleCard(teamMembers);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildRoleCard(List<ProjectAllocation> teamMembers) {
|
||||||
|
teamMembers.sort((a, b) {
|
||||||
|
final aName = ("${a.firstName} ${a.lastName}").trim().toLowerCase();
|
||||||
|
final bName = ("${b.firstName} ${b.lastName}").trim().toLowerCase();
|
||||||
|
return aName.compareTo(bName);
|
||||||
|
});
|
||||||
|
|
||||||
|
final String roleName =
|
||||||
|
(teamMembers.isNotEmpty ? (teamMembers.first.jobRoleName) : '').trim();
|
||||||
|
|
||||||
|
return Card(
|
||||||
|
elevation: 3,
|
||||||
|
shadowColor: Colors.black26,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
// TOP: Job Role name
|
||||||
|
if (roleName.isNotEmpty) ...[
|
||||||
|
MyText.bodyLarge(
|
||||||
|
roleName,
|
||||||
|
fontWeight: 700,
|
||||||
|
),
|
||||||
|
const Divider(height: 20),
|
||||||
|
] else
|
||||||
|
const Divider(height: 20),
|
||||||
|
|
||||||
|
// Team members list
|
||||||
|
...teamMembers.map((allocation) {
|
||||||
|
return InkWell(
|
||||||
|
onTap: () {
|
||||||
|
Get.to(
|
||||||
|
() => EmployeeProfilePage(
|
||||||
|
employeeId: allocation.employeeId,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Avatar(
|
||||||
|
firstName: allocation.firstName,
|
||||||
|
lastName: allocation.lastName,
|
||||||
|
size: 32,
|
||||||
|
),
|
||||||
|
MySpacing.width(12),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
MyText.titleMedium(
|
||||||
|
"${allocation.firstName} ${allocation.lastName}",
|
||||||
|
fontWeight: 600,
|
||||||
|
),
|
||||||
|
MyText.bodySmall(
|
||||||
|
allocation.serviceName.isNotEmpty
|
||||||
|
? "Service: ${allocation.serviceName}"
|
||||||
|
: "No Service Assigned",
|
||||||
|
color: Colors.grey[700],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
MyText.bodySmall(
|
||||||
|
"Allocated",
|
||||||
|
color: Colors.grey.shade500,
|
||||||
|
),
|
||||||
|
MyText.bodySmall(
|
||||||
|
DateFormat('d MMM yyyy').format(
|
||||||
|
DateTime.parse(allocation.allocationDate),
|
||||||
|
),
|
||||||
|
fontWeight: 600,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildProfileTab() {
|
||||||
return Obx(() {
|
return Obx(() {
|
||||||
if (controller.isLoading.value) {
|
if (controller.isLoading.value) {
|
||||||
return const Center(child: CircularProgressIndicator());
|
return const Center(child: CircularProgressIndicator());
|
||||||
@ -155,23 +299,27 @@ class _InfraProjectDetailsScreenState extends State<InfraProjectDetailsScreen>
|
|||||||
_buildDetailRow(
|
_buildDetailRow(
|
||||||
icon: Icons.location_on_outlined,
|
icon: Icons.location_on_outlined,
|
||||||
label: 'Address',
|
label: 'Address',
|
||||||
value: data.projectAddress ?? "-"),
|
value: data.projectAddress ?? "-",
|
||||||
|
),
|
||||||
_buildDetailRow(
|
_buildDetailRow(
|
||||||
icon: Icons.calendar_today_outlined,
|
icon: Icons.calendar_today_outlined,
|
||||||
label: 'Start Date',
|
label: 'Start Date',
|
||||||
value: data.startDate != null
|
value: data.startDate != null
|
||||||
? DateFormat('d/M/yyyy').format(data.startDate!)
|
? DateFormat('d/M/yyyy').format(data.startDate!)
|
||||||
: "-"),
|
: "-",
|
||||||
|
),
|
||||||
_buildDetailRow(
|
_buildDetailRow(
|
||||||
icon: Icons.calendar_today_outlined,
|
icon: Icons.calendar_today_outlined,
|
||||||
label: 'End Date',
|
label: 'End Date',
|
||||||
value: data.endDate != null
|
value: data.endDate != null
|
||||||
? DateFormat('d/M/yyyy').format(data.endDate!)
|
? DateFormat('d/M/yyyy').format(data.endDate!)
|
||||||
: "-"),
|
: "-",
|
||||||
|
),
|
||||||
_buildDetailRow(
|
_buildDetailRow(
|
||||||
icon: Icons.flag_outlined,
|
icon: Icons.flag_outlined,
|
||||||
label: 'Status',
|
label: 'Status',
|
||||||
value: data.projectStatus?.status ?? "-"),
|
value: data.projectStatus?.status ?? "-",
|
||||||
|
),
|
||||||
_buildDetailRow(
|
_buildDetailRow(
|
||||||
icon: Icons.person_outline,
|
icon: Icons.person_outline,
|
||||||
label: 'Contact Person',
|
label: 'Contact Person',
|
||||||
@ -181,7 +329,8 @@ class _InfraProjectDetailsScreenState extends State<InfraProjectDetailsScreen>
|
|||||||
if (data.contactPerson != null) {
|
if (data.contactPerson != null) {
|
||||||
LauncherUtils.launchPhone(data.contactPerson!);
|
LauncherUtils.launchPhone(data.contactPerson!);
|
||||||
}
|
}
|
||||||
}),
|
},
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -194,20 +343,22 @@ class _InfraProjectDetailsScreenState extends State<InfraProjectDetailsScreen>
|
|||||||
_buildDetailRow(
|
_buildDetailRow(
|
||||||
icon: Icons.person_outline,
|
icon: Icons.person_outline,
|
||||||
label: 'Name',
|
label: 'Name',
|
||||||
value: promoter.name ?? "-"),
|
value: promoter.name ?? "-",
|
||||||
|
),
|
||||||
_buildDetailRow(
|
_buildDetailRow(
|
||||||
icon: Icons.phone_outlined,
|
icon: Icons.phone_outlined,
|
||||||
label: 'Contact',
|
label: 'Contact',
|
||||||
value: promoter.contactNumber ?? "-",
|
value: promoter.contactNumber ?? "-",
|
||||||
isActionable: true,
|
isActionable: true,
|
||||||
onTap: () =>
|
onTap: () => LauncherUtils.launchPhone(promoter.contactNumber ?? ""),
|
||||||
LauncherUtils.launchPhone(promoter.contactNumber ?? "")),
|
),
|
||||||
_buildDetailRow(
|
_buildDetailRow(
|
||||||
icon: Icons.email_outlined,
|
icon: Icons.email_outlined,
|
||||||
label: 'Email',
|
label: 'Email',
|
||||||
value: promoter.email ?? "-",
|
value: promoter.email ?? "-",
|
||||||
isActionable: true,
|
isActionable: true,
|
||||||
onTap: () => LauncherUtils.launchEmail(promoter.email ?? "")),
|
onTap: () => LauncherUtils.launchEmail(promoter.email ?? ""),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -218,19 +369,24 @@ class _InfraProjectDetailsScreenState extends State<InfraProjectDetailsScreen>
|
|||||||
titleIcon: Icons.engineering_outlined,
|
titleIcon: Icons.engineering_outlined,
|
||||||
children: [
|
children: [
|
||||||
_buildDetailRow(
|
_buildDetailRow(
|
||||||
icon: Icons.person_outline, label: 'Name', value: pmc.name ?? "-"),
|
icon: Icons.person_outline,
|
||||||
|
label: 'Name',
|
||||||
|
value: pmc.name ?? "-",
|
||||||
|
),
|
||||||
_buildDetailRow(
|
_buildDetailRow(
|
||||||
icon: Icons.phone_outlined,
|
icon: Icons.phone_outlined,
|
||||||
label: 'Contact',
|
label: 'Contact',
|
||||||
value: pmc.contactNumber ?? "-",
|
value: pmc.contactNumber ?? "-",
|
||||||
isActionable: true,
|
isActionable: true,
|
||||||
onTap: () => LauncherUtils.launchPhone(pmc.contactNumber ?? "")),
|
onTap: () => LauncherUtils.launchPhone(pmc.contactNumber ?? ""),
|
||||||
|
),
|
||||||
_buildDetailRow(
|
_buildDetailRow(
|
||||||
icon: Icons.email_outlined,
|
icon: Icons.email_outlined,
|
||||||
label: 'Email',
|
label: 'Email',
|
||||||
value: pmc.email ?? "-",
|
value: pmc.email ?? "-",
|
||||||
isActionable: true,
|
isActionable: true,
|
||||||
onTap: () => LauncherUtils.launchEmail(pmc.email ?? "")),
|
onTap: () => LauncherUtils.launchEmail(pmc.email ?? ""),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -251,7 +407,9 @@ class _InfraProjectDetailsScreenState extends State<InfraProjectDetailsScreen>
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.all(8), child: Icon(icon, size: 20)),
|
padding: const EdgeInsets.all(8),
|
||||||
|
child: Icon(icon, size: 20),
|
||||||
|
),
|
||||||
MySpacing.width(16),
|
MySpacing.width(16),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user