feat: Refactor employee detail navigation and add employee profile screen with tabbed interface

This commit is contained in:
Vaibhav Surve 2025-09-04 17:41:11 +05:30
parent 334023bf1b
commit 2133dedfae
4 changed files with 115 additions and 88 deletions

View File

@ -139,7 +139,7 @@ class _UserDocumentsPageState extends State<UserDocumentsPage> {
),
IconButton(
icon: const Icon(Icons.arrow_forward_ios, color: Colors.black54),
onPressed: () {/* future actions */},
onPressed: () {},
),
],
),
@ -257,14 +257,19 @@ class _UserDocumentsPageState extends State<UserDocumentsPage> {
@override
Widget build(BuildContext context) {
// Conditionally show AppBar (example: hide if employee view)
final bool showAppBar = !widget.isEmployee;
return Scaffold(
backgroundColor: const Color(0xFFF1F1F1),
appBar: CustomAppBar(
title: 'Documents',
onBackPressed: () {
Get.back();
},
),
appBar: showAppBar
? CustomAppBar(
title: 'Documents',
onBackPressed: () {
Get.back();
},
)
: null,
body: _buildBody(context),
floatingActionButton: FloatingActionButton.extended(
onPressed: () {
@ -289,17 +294,13 @@ class _UserDocumentsPageState extends State<UserDocumentsPage> {
);
if (success) {
// Only close on success
Navigator.pop(context);
// Refresh list
docController.fetchDocuments(
entityTypeId: entityTypeId,
entityId: resolvedEntityId,
reset: true,
);
} else {
// Dont close, show error
Get.snackbar("Error", "Upload failed, please try again");
}
},

View File

@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
import 'package:marco/controller/employee/employees_screen_controller.dart';
import 'package:marco/controller/project_controller.dart';
import 'package:marco/helpers/widgets/custom_app_bar.dart';
import 'package:marco/helpers/widgets/avatar.dart';
import 'package:marco/helpers/widgets/my_spacing.dart';
import 'package:marco/helpers/widgets/my_text.dart';
@ -15,6 +15,7 @@ import 'package:marco/helpers/widgets/my_refresh_indicator.dart';
class EmployeeDetailPage extends StatefulWidget {
final String employeeId;
final bool fromProfile;
const EmployeeDetailPage({
super.key,
required this.employeeId,
@ -30,6 +31,7 @@ class _EmployeeDetailPageState extends State<EmployeeDetailPage> {
Get.put(EmployeesScreenController());
final PermissionController _permissionController =
Get.find<PermissionController>();
@override
void initState() {
super.initState();
@ -60,7 +62,6 @@ class _EmployeeDetailPageState extends State<EmployeeDetailPage> {
}
}
/// Row builder with email/phone tap & copy support
Widget _buildLabelValueRow(String label, String value,
{bool isMultiLine = false}) {
final lowerLabel = label.toLowerCase();
@ -91,9 +92,8 @@ class _EmployeeDetailPageState extends State<EmployeeDetailPage> {
fontWeight: FontWeight.normal,
color: (isEmail || isPhone) ? Colors.indigo : Colors.black54,
fontSize: 14,
decoration: (isEmail || isPhone)
? TextDecoration.underline
: TextDecoration.none,
decoration:
(isEmail || isPhone) ? TextDecoration.underline : TextDecoration.none,
),
),
);
@ -147,7 +147,6 @@ class _EmployeeDetailPageState extends State<EmployeeDetailPage> {
);
}
/// Info card
Widget _buildInfoCard(employee) {
return Card(
elevation: 3,
@ -188,73 +187,22 @@ class _EmployeeDetailPageState extends State<EmployeeDetailPage> {
@override
Widget build(BuildContext context) {
final bool showAppBar = !widget.fromProfile;
return Scaffold(
backgroundColor: const Color(0xFFF1F1F1),
appBar: PreferredSize(
preferredSize: const Size.fromHeight(72),
child: AppBar(
backgroundColor: const Color(0xFFF5F5F5),
elevation: 0.5,
automaticallyImplyLeading: false,
titleSpacing: 0,
title: Padding(
padding: MySpacing.xy(16, 0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
IconButton(
icon: const Icon(Icons.arrow_back_ios_new,
color: Colors.black, size: 20),
onPressed: () {
if (widget.fromProfile) {
Get.back();
} else {
Get.offNamed('/dashboard/employees');
}
},
),
MySpacing.width(8),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
MyText.titleLarge(
'Employee Details',
fontWeight: 700,
color: Colors.black,
),
MySpacing.height(2),
GetBuilder<ProjectController>(
builder: (projectController) {
final projectName =
projectController.selectedProject?.name ??
'Select Project';
return Row(
children: [
const Icon(Icons.work_outline,
size: 14, color: Colors.grey),
MySpacing.width(4),
Expanded(
child: MyText.bodySmall(
projectName,
fontWeight: 600,
overflow: TextOverflow.ellipsis,
color: Colors.grey[700],
),
),
],
);
},
),
],
),
),
],
),
),
),
),
appBar: showAppBar
? CustomAppBar(
title: 'Employee Details',
onBackPressed: () {
if (widget.fromProfile) {
Get.back();
} else {
Get.offNamed('/dashboard/employees');
}
},
)
: null,
body: Obx(() {
if (controller.isLoadingEmployeeDetails.value) {
return const Center(child: CircularProgressIndicator());

View File

@ -0,0 +1,80 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:marco/view/employees/employee_detail_screen.dart';
import 'package:marco/view/document/user_document_screen.dart';
import 'package:marco/helpers/widgets/custom_app_bar.dart';
class EmployeeProfilePage extends StatefulWidget {
final String employeeId;
const EmployeeProfilePage({super.key, required this.employeeId});
@override
State<EmployeeProfilePage> createState() => _EmployeeProfilePageState();
}
class _EmployeeProfilePageState extends State<EmployeeProfilePage>
with SingleTickerProviderStateMixin {
late TabController _tabController;
@override
void initState() {
super.initState();
_tabController = TabController(length: 2, vsync: this);
}
@override
void dispose() {
_tabController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFFF1F1F1),
appBar: CustomAppBar(
title: "Employee Profile",
onBackPressed: () => Get.back(),
),
body: Column(
children: [
// ---------------- TabBar outside AppBar ----------------
Container(
color: Colors.white,
child: TabBar(
controller: _tabController,
labelColor: Colors.black,
unselectedLabelColor: Colors.grey,
indicatorColor: Colors.red,
tabs: const [
Tab(text: "Details"),
Tab(text: "Documents"),
],
),
),
// ---------------- TabBarView ----------------
Expanded(
child: TabBarView(
controller: _tabController,
children: [
// Details Tab
EmployeeDetailPage(
employeeId: widget.employeeId,
fromProfile: true,
),
// Documents Tab
UserDocumentsPage(
entityId: widget.employeeId,
isEmployee: true,
),
],
),
),
],
),
);
}
}

View File

@ -9,10 +9,9 @@ import 'package:marco/helpers/widgets/my_text.dart';
import 'package:marco/helpers/widgets/avatar.dart';
import 'package:marco/model/employees/employee_info.dart';
import 'package:marco/controller/auth/mpin_controller.dart';
import 'package:marco/view/employees/employee_detail_screen.dart';
import 'package:marco/view/employees/employee_profile_screen.dart';
import 'package:marco/view/document/user_document_screen.dart';
class UserProfileBar extends StatefulWidget {
final bool isCondensed;
const UserProfileBar({Key? key, this.isCondensed = false}) : super(key: key);
@ -182,7 +181,7 @@ class _UserProfileBarState extends State<UserProfileBar>
_menuItemRow(
icon: LucideIcons.file_text,
label: 'My Documents',
onTap: _onDocumentsTap,
onTap: _onDocumentsTap,
),
SizedBox(height: spacingHeight),
_menuItemRow(
@ -246,15 +245,14 @@ class _UserProfileBarState extends State<UserProfileBar>
}
void _onProfileTap() {
Get.to(() => EmployeeDetailPage(
Get.to(() => EmployeeProfilePage(
employeeId: employeeInfo.id,
fromProfile: true,
));
}
void _onDocumentsTap() {
Get.to(() => UserDocumentsPage(
entityId: "${employeeInfo.id}",
entityId: "${employeeInfo.id}",
isEmployee: true,
));
}