marco.pms.mobileapp/lib/model/employees/employee_detail_bottom_sheet.dart
Vaibhav Surve f5d4ab8415 feat: Add attendance log screen and related functionalities
- Implemented AttendenceLogScreen to display employee attendance logs.
- Created RegularizationRequestsTab to manage regularization requests.
- Added TodaysAttendanceTab for viewing today's attendance.
- Removed outdated dashboard chart implementation.
- Updated dashboard screen to integrate new attendance overview and project progress charts.
- Refactored employee detail and employee screens to use updated controllers.
- Organized expense-related imports and components for better structure.
- Adjusted daily progress report to use the correct controller.
2025-08-29 15:53:19 +05:30

258 lines
8.9 KiB
Dart

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:marco/helpers/widgets/my_spacing.dart';
import 'package:marco/helpers/widgets/my_text.dart';
import 'package:marco/helpers/widgets/avatar.dart';
import 'package:intl/intl.dart';
import 'package:marco/controller/employee/employees_screen_controller.dart';
import 'package:marco/view/employees/assign_employee_bottom_sheet.dart';
class EmployeeDetailBottomSheet extends StatefulWidget {
final String employeeId;
const EmployeeDetailBottomSheet({super.key, required this.employeeId});
@override
State<EmployeeDetailBottomSheet> createState() =>
_EmployeeDetailBottomSheetState();
}
class _EmployeeDetailBottomSheetState extends State<EmployeeDetailBottomSheet> {
final EmployeesScreenController controller =
Get.put(EmployeesScreenController());
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
controller.fetchEmployeeDetails(widget.employeeId);
});
}
@override
void dispose() {
controller.selectedEmployeeDetails.value = null;
super.dispose();
}
String _getDisplayValue(dynamic value) {
if (value == null || value.toString().trim().isEmpty || value == 'null') {
return 'NA';
}
return value.toString();
}
String _formatDate(DateTime? date) {
if (date == null || date == DateTime(1)) return 'NA';
try {
return DateFormat('d/M/yyyy').format(date);
} catch (_) {
return 'NA';
}
}
Widget _buildLabelValueRow(IconData icon, String label, String value) {
return Padding(
padding: const EdgeInsets.only(bottom: 12),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(icon, size: 20, color: Colors.grey[700]),
MySpacing.width(8),
MyText.bodyMedium('$label:', fontWeight: 600),
MySpacing.width(8),
Expanded(child: MyText.bodyMedium(value, fontWeight: 400)),
],
),
);
}
@override
Widget build(BuildContext context) {
return DraggableScrollableSheet(
expand: false,
maxChildSize: 0.85,
minChildSize: 0.4,
initialChildSize: 0.6,
builder: (_, controllerScroll) {
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: const BorderRadius.vertical(top: Radius.circular(16)),
boxShadow: const [
BoxShadow(
color: Colors.black26,
blurRadius: 10,
offset: Offset(0, -3),
),
],
),
child: Obx(() {
if (controller.isLoadingEmployeeDetails.value) {
return const Center(child: CircularProgressIndicator());
}
final employee = controller.selectedEmployeeDetails.value;
if (employee == null) {
return const Center(child: Text('No employee details found.'));
}
return SingleChildScrollView(
controller: controllerScroll,
padding: MySpacing.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// Drag Handle
Container(
width: 50,
height: 5,
decoration: BoxDecoration(
color: Colors.grey[400],
borderRadius: BorderRadius.circular(3),
),
),
MySpacing.height(20),
// Row 1: Avatar + Name
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
CircleAvatar(
radius: 40,
backgroundColor: Colors.blueGrey[200],
child: Avatar(
firstName: employee.firstName,
lastName: employee.lastName,
size: 60,
),
),
MySpacing.width(16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
MyText.titleLarge(
'${employee.firstName} ${employee.lastName}',
fontWeight: 700,
),
MySpacing.height(6),
MyText.bodyMedium(
_getDisplayValue(employee.jobRole),
fontWeight: 500,
color: Colors.grey[700],
),
],
),
),
],
),
MySpacing.height(12),
// Row 2: Minimal Button
Align(
alignment: Alignment.centerRight,
child: TextButton(
style: TextButton.styleFrom(
padding: const EdgeInsets.symmetric(
horizontal: 10, vertical: 6),
backgroundColor: Colors.blueAccent,
minimumSize: const Size(0, 32),
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(6),
),
),
onPressed: () {
showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (context) => AssignProjectBottomSheet(
employeeId: widget.employeeId,
jobRoleId: employee.jobRoleId,
),
);
},
child: const Text(
'Assign to Project',
style: TextStyle(
color: Colors.white,
fontSize: 13,
fontWeight: FontWeight.w500,
),
),
),
),
MySpacing.height(20),
// Contact Info Card
_buildInfoCard('Contact Information', [
_buildLabelValueRow(
Icons.email, 'Email', _getDisplayValue(employee.email)),
_buildLabelValueRow(Icons.phone, 'Phone Number',
_getDisplayValue(employee.phoneNumber)),
]),
MySpacing.height(10),
// Emergency Contact Info Card
_buildInfoCard('Emergency Contact', [
_buildLabelValueRow(Icons.person, 'Contact Person',
_getDisplayValue(employee.emergencyContactPerson)),
_buildLabelValueRow(Icons.phone_android, 'Contact Number',
_getDisplayValue(employee.emergencyPhoneNumber)),
]),
MySpacing.height(10),
// Personal Info Card
_buildInfoCard('Personal Information', [
_buildLabelValueRow(Icons.transgender, 'Gender',
_getDisplayValue(employee.gender)),
_buildLabelValueRow(Icons.cake, 'Birth Date',
_formatDate(employee.birthDate)),
_buildLabelValueRow(Icons.work, 'Joining Date',
_formatDate(employee.joiningDate)),
]),
MySpacing.height(10),
// Address Card
_buildInfoCard('Address', [
_buildLabelValueRow(Icons.home, 'Current Address',
_getDisplayValue(employee.currentAddress)),
_buildLabelValueRow(Icons.home_filled, 'Permanent Address',
_getDisplayValue(employee.permanentAddress)),
]),
],
),
);
}),
);
},
);
}
Widget _buildInfoCard(String title, List<Widget> children) {
return Card(
elevation: 1,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
MyText.titleMedium(title, fontWeight: 700),
MySpacing.height(12),
...children,
],
),
),
);
}
}