marco.pms.mobileapp/lib/view/layouts/user_profile_right_bar.dart
Vaibhav Surve 91184b48bb Refactor employee model imports and restructure employee-related files
- Updated import paths for employee model files to reflect new directory structure.
- Deleted obsolete models: JobRecentApplicationModel, LeadReportModel, Product, ProductOrderModal, ProjectSummaryModel, RecentOrderModel, TaskListModel, TimeLineModel, User, VisitorByChannelsModel.
- Introduced new AttendanceLogModel, AttendanceLogViewModel, AttendanceModel, TaskModel, TaskListModel, EmployeeInfo, and EmployeeModel with comprehensive fields and JSON serialization methods.
- Enhanced data handling in attendance and task management features.
2025-08-26 11:53:53 +05:30

335 lines
10 KiB
Dart

import 'package:flutter/material.dart';
import 'package:marco/helpers/theme/theme_customizer.dart';
import 'package:marco/helpers/services/storage/local_storage.dart';
import 'package:marco/helpers/utils/mixins/ui_mixin.dart';
import 'package:marco/helpers/utils/my_shadow.dart';
import 'package:marco/helpers/widgets/my_card.dart';
import 'package:marco/helpers/widgets/my_spacing.dart';
import 'package:marco/helpers/widgets/my_text.dart';
import 'package:flutter_lucide/flutter_lucide.dart';
import 'package:marco/model/employees/employee_info.dart';
import 'package:marco/helpers/widgets/avatar.dart';
import 'package:get/get.dart';
import 'package:marco/controller/auth/mpin_controller.dart';
class UserProfileBar extends StatefulWidget {
final bool isCondensed;
const UserProfileBar({super.key, this.isCondensed = false});
@override
_UserProfileBarState createState() => _UserProfileBarState();
}
class _UserProfileBarState extends State<UserProfileBar>
with SingleTickerProviderStateMixin, UIMixin {
final ThemeCustomizer customizer = ThemeCustomizer.instance;
bool isCondensed = false;
EmployeeInfo? employeeInfo;
bool hasMpin = true;
@override
void initState() {
super.initState();
_loadEmployeeInfo();
_checkMpinStatus();
}
void _loadEmployeeInfo() {
setState(() {
employeeInfo = LocalStorage.getEmployeeInfo();
});
}
Future<void> _checkMpinStatus() async {
final bool mpinStatus = await LocalStorage.getIsMpin();
setState(() {
hasMpin = mpinStatus;
});
}
@override
Widget build(BuildContext context) {
isCondensed = widget.isCondensed;
return MyCard(
borderRadiusAll: 16,
paddingAll: 0,
shadow: MyShadow(
position: MyShadowPosition.centerRight,
elevation: 6,
blurRadius: 12,
),
child: AnimatedContainer(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
leftBarTheme.background.withOpacity(0.97),
leftBarTheme.background.withOpacity(0.88),
],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
),
width: isCondensed ? 90 : 260,
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
child: SafeArea(
bottom: true,
top: false,
left: false,
right: false,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
userProfileSection(),
MySpacing.height(16),
supportAndSettingsMenu(),
const Spacer(),
logoutButton(),
],
),
),
),
);
}
/// User Profile Section - Avatar + Name
Widget userProfileSection() {
if (employeeInfo == null) {
return const Padding(
padding: EdgeInsets.symmetric(vertical: 32),
child: Center(child: CircularProgressIndicator()),
);
}
return Container(
width: double.infinity,
padding: MySpacing.fromLTRB(20, 50, 30, 50),
decoration: BoxDecoration(
color: leftBarTheme.activeItemBackground,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(16),
topRight: Radius.circular(16),
),
),
child: Row(
children: [
Avatar(
firstName: employeeInfo?.firstName ?? 'F',
lastName: employeeInfo?.lastName ?? 'N',
size: 50,
),
MySpacing.width(12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
MyText.bodyMedium(
"${employeeInfo?.firstName ?? 'First'} ${employeeInfo?.lastName ?? 'Last'}",
fontWeight: 700,
color: leftBarTheme.activeItemColor,
),
],
),
),
],
),
);
}
/// Menu Section with Settings, Support & MPIN
Widget supportAndSettingsMenu() {
return Padding(
padding: MySpacing.xy(16, 16),
child: Column(
children: [
menuItem(
icon: LucideIcons.settings,
label: "Settings",
),
MySpacing.height(14),
menuItem(
icon: LucideIcons.badge_help,
label: "Support",
),
MySpacing.height(14),
menuItem(
icon: LucideIcons.lock,
label: hasMpin ? "Change MPIN" : "Set MPIN",
iconColor: hasMpin ? leftBarTheme.onBackground : Colors.redAccent,
labelColor: hasMpin ? leftBarTheme.onBackground : Colors.redAccent,
onTap: () {
final controller = Get.put(MPINController());
if (hasMpin) {
controller.setChangeMpinMode();
}
Navigator.pushNamed(context, "/auth/mpin-auth");
},
filled: true,
),
],
),
);
}
Widget menuItem({
required IconData icon,
required String label,
Color? iconColor,
Color? labelColor,
VoidCallback? onTap,
bool filled = false,
}) {
return InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(12),
hoverColor: leftBarTheme.activeItemBackground.withOpacity(0.25),
splashColor: leftBarTheme.activeItemBackground.withOpacity(0.35),
child: Container(
padding: MySpacing.xy(14, 12),
decoration: BoxDecoration(
color: filled
? leftBarTheme.activeItemBackground.withOpacity(0.15)
: Colors.transparent,
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: filled
? leftBarTheme.activeItemBackground.withOpacity(0.3)
: Colors.transparent,
width: 1,
),
),
child: Row(
children: [
Icon(icon, size: 22, color: iconColor ?? leftBarTheme.onBackground),
MySpacing.width(14),
Expanded(
child: MyText.bodyMedium(
label,
color: labelColor ?? leftBarTheme.onBackground,
fontWeight: 600,
),
),
],
),
),
);
}
/// Logout Button
Widget logoutButton() {
return InkWell(
onTap: () async {
await _showLogoutConfirmation();
},
borderRadius: const BorderRadius.only(
bottomLeft: Radius.circular(16),
bottomRight: Radius.circular(16),
),
hoverColor: leftBarTheme.activeItemBackground.withOpacity(0.25),
splashColor: leftBarTheme.activeItemBackground.withOpacity(0.35),
child: Container(
padding: MySpacing.all(16),
decoration: BoxDecoration(
color: leftBarTheme.activeItemBackground,
borderRadius: const BorderRadius.only(
bottomLeft: Radius.circular(16),
bottomRight: Radius.circular(16),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
MyText.bodyMedium(
"Logout",
color: leftBarTheme.activeItemColor,
fontWeight: 600,
),
MySpacing.width(8),
Icon(
LucideIcons.log_out,
size: 20,
color: leftBarTheme.activeItemColor,
),
],
),
),
);
}
Future<void> _showLogoutConfirmation() async {
bool? confirm = await showDialog<bool>(
context: context,
builder: (context) => _buildLogoutDialog(context),
);
if (confirm == true) {
await LocalStorage.logout();
}
}
Widget _buildLogoutDialog(BuildContext context) {
return Dialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 28),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(LucideIcons.log_out, size: 48, color: Colors.redAccent),
const SizedBox(height: 16),
Text(
"Logout Confirmation",
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w700,
color: Theme.of(context).colorScheme.onBackground,
),
),
const SizedBox(height: 12),
Text(
"Are you sure you want to logout?\nYou will need to login again to continue.",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14,
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.7),
),
),
const SizedBox(height: 24),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: TextButton(
onPressed: () => Navigator.pop(context, false),
style: TextButton.styleFrom(
foregroundColor: Colors.grey.shade700,
),
child: const Text("Cancel"),
),
),
const SizedBox(width: 12),
Expanded(
child: ElevatedButton(
onPressed: () => Navigator.pop(context, true),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.redAccent,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
child: const Text("Logout"),
),
),
],
),
],
),
),
);
}
}