feat: Refactor employee detail navigation and add employee profile screen with tabbed interface
This commit is contained in:
parent
334023bf1b
commit
2133dedfae
@ -139,7 +139,7 @@ class _UserDocumentsPageState extends State<UserDocumentsPage> {
|
|||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.arrow_forward_ios, color: Colors.black54),
|
icon: const Icon(Icons.arrow_forward_ios, color: Colors.black54),
|
||||||
onPressed: () {/* future actions */},
|
onPressed: () {},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -257,14 +257,19 @@ class _UserDocumentsPageState extends State<UserDocumentsPage> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
// Conditionally show AppBar (example: hide if employee view)
|
||||||
|
final bool showAppBar = !widget.isEmployee;
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: const Color(0xFFF1F1F1),
|
backgroundColor: const Color(0xFFF1F1F1),
|
||||||
appBar: CustomAppBar(
|
appBar: showAppBar
|
||||||
title: 'Documents',
|
? CustomAppBar(
|
||||||
onBackPressed: () {
|
title: 'Documents',
|
||||||
Get.back();
|
onBackPressed: () {
|
||||||
},
|
Get.back();
|
||||||
),
|
},
|
||||||
|
)
|
||||||
|
: null,
|
||||||
body: _buildBody(context),
|
body: _buildBody(context),
|
||||||
floatingActionButton: FloatingActionButton.extended(
|
floatingActionButton: FloatingActionButton.extended(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
@ -289,17 +294,13 @@ class _UserDocumentsPageState extends State<UserDocumentsPage> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
// ✅ Only close on success
|
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
|
|
||||||
// Refresh list
|
|
||||||
docController.fetchDocuments(
|
docController.fetchDocuments(
|
||||||
entityTypeId: entityTypeId,
|
entityTypeId: entityTypeId,
|
||||||
entityId: resolvedEntityId,
|
entityId: resolvedEntityId,
|
||||||
reset: true,
|
reset: true,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
// ❌ Don’t close, show error
|
|
||||||
Get.snackbar("Error", "Upload failed, please try again");
|
Get.snackbar("Error", "Upload failed, please try again");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:marco/controller/employee/employees_screen_controller.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/avatar.dart';
|
||||||
import 'package:marco/helpers/widgets/my_spacing.dart';
|
import 'package:marco/helpers/widgets/my_spacing.dart';
|
||||||
import 'package:marco/helpers/widgets/my_text.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 {
|
class EmployeeDetailPage extends StatefulWidget {
|
||||||
final String employeeId;
|
final String employeeId;
|
||||||
final bool fromProfile;
|
final bool fromProfile;
|
||||||
|
|
||||||
const EmployeeDetailPage({
|
const EmployeeDetailPage({
|
||||||
super.key,
|
super.key,
|
||||||
required this.employeeId,
|
required this.employeeId,
|
||||||
@ -30,6 +31,7 @@ class _EmployeeDetailPageState extends State<EmployeeDetailPage> {
|
|||||||
Get.put(EmployeesScreenController());
|
Get.put(EmployeesScreenController());
|
||||||
final PermissionController _permissionController =
|
final PermissionController _permissionController =
|
||||||
Get.find<PermissionController>();
|
Get.find<PermissionController>();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.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,
|
Widget _buildLabelValueRow(String label, String value,
|
||||||
{bool isMultiLine = false}) {
|
{bool isMultiLine = false}) {
|
||||||
final lowerLabel = label.toLowerCase();
|
final lowerLabel = label.toLowerCase();
|
||||||
@ -91,9 +92,8 @@ class _EmployeeDetailPageState extends State<EmployeeDetailPage> {
|
|||||||
fontWeight: FontWeight.normal,
|
fontWeight: FontWeight.normal,
|
||||||
color: (isEmail || isPhone) ? Colors.indigo : Colors.black54,
|
color: (isEmail || isPhone) ? Colors.indigo : Colors.black54,
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
decoration: (isEmail || isPhone)
|
decoration:
|
||||||
? TextDecoration.underline
|
(isEmail || isPhone) ? TextDecoration.underline : TextDecoration.none,
|
||||||
: TextDecoration.none,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -147,7 +147,6 @@ class _EmployeeDetailPageState extends State<EmployeeDetailPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Info card
|
|
||||||
Widget _buildInfoCard(employee) {
|
Widget _buildInfoCard(employee) {
|
||||||
return Card(
|
return Card(
|
||||||
elevation: 3,
|
elevation: 3,
|
||||||
@ -188,73 +187,22 @@ class _EmployeeDetailPageState extends State<EmployeeDetailPage> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final bool showAppBar = !widget.fromProfile;
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: const Color(0xFFF1F1F1),
|
backgroundColor: const Color(0xFFF1F1F1),
|
||||||
appBar: PreferredSize(
|
appBar: showAppBar
|
||||||
preferredSize: const Size.fromHeight(72),
|
? CustomAppBar(
|
||||||
child: AppBar(
|
title: 'Employee Details',
|
||||||
backgroundColor: const Color(0xFFF5F5F5),
|
onBackPressed: () {
|
||||||
elevation: 0.5,
|
if (widget.fromProfile) {
|
||||||
automaticallyImplyLeading: false,
|
Get.back();
|
||||||
titleSpacing: 0,
|
} else {
|
||||||
title: Padding(
|
Get.offNamed('/dashboard/employees');
|
||||||
padding: MySpacing.xy(16, 0),
|
}
|
||||||
child: Row(
|
},
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
)
|
||||||
children: [
|
: null,
|
||||||
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],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
body: Obx(() {
|
body: Obx(() {
|
||||||
if (controller.isLoadingEmployeeDetails.value) {
|
if (controller.isLoadingEmployeeDetails.value) {
|
||||||
return const Center(child: CircularProgressIndicator());
|
return const Center(child: CircularProgressIndicator());
|
||||||
|
|||||||
80
lib/view/employees/employee_profile_screen.dart
Normal file
80
lib/view/employees/employee_profile_screen.dart
Normal 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,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -9,10 +9,9 @@ import 'package:marco/helpers/widgets/my_text.dart';
|
|||||||
import 'package:marco/helpers/widgets/avatar.dart';
|
import 'package:marco/helpers/widgets/avatar.dart';
|
||||||
import 'package:marco/model/employees/employee_info.dart';
|
import 'package:marco/model/employees/employee_info.dart';
|
||||||
import 'package:marco/controller/auth/mpin_controller.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';
|
import 'package:marco/view/document/user_document_screen.dart';
|
||||||
|
|
||||||
|
|
||||||
class UserProfileBar extends StatefulWidget {
|
class UserProfileBar extends StatefulWidget {
|
||||||
final bool isCondensed;
|
final bool isCondensed;
|
||||||
const UserProfileBar({Key? key, this.isCondensed = false}) : super(key: key);
|
const UserProfileBar({Key? key, this.isCondensed = false}) : super(key: key);
|
||||||
@ -182,7 +181,7 @@ class _UserProfileBarState extends State<UserProfileBar>
|
|||||||
_menuItemRow(
|
_menuItemRow(
|
||||||
icon: LucideIcons.file_text,
|
icon: LucideIcons.file_text,
|
||||||
label: 'My Documents',
|
label: 'My Documents',
|
||||||
onTap: _onDocumentsTap,
|
onTap: _onDocumentsTap,
|
||||||
),
|
),
|
||||||
SizedBox(height: spacingHeight),
|
SizedBox(height: spacingHeight),
|
||||||
_menuItemRow(
|
_menuItemRow(
|
||||||
@ -246,15 +245,14 @@ class _UserProfileBarState extends State<UserProfileBar>
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _onProfileTap() {
|
void _onProfileTap() {
|
||||||
Get.to(() => EmployeeDetailPage(
|
Get.to(() => EmployeeProfilePage(
|
||||||
employeeId: employeeInfo.id,
|
employeeId: employeeInfo.id,
|
||||||
fromProfile: true,
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onDocumentsTap() {
|
void _onDocumentsTap() {
|
||||||
Get.to(() => UserDocumentsPage(
|
Get.to(() => UserDocumentsPage(
|
||||||
entityId: "${employeeInfo.id}",
|
entityId: "${employeeInfo.id}",
|
||||||
isEmployee: true,
|
isEmployee: true,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user