diff --git a/apps/edr-freight-web/backoffice/src/record-management/components/common/Header.tsx b/apps/edr-freight-web/backoffice/src/record-management/components/common/Header.tsx deleted file mode 100644 index 901001c93..000000000 --- a/apps/edr-freight-web/backoffice/src/record-management/components/common/Header.tsx +++ /dev/null @@ -1,413 +0,0 @@ -import React, { useMemo } from "react"; -import { useTranslation } from "react-i18next"; -import { NavLink, useLocation } from "react-router-dom"; -import { cn } from "@/shared/lib/utils"; -import { - LayoutGrid, - Upload, - Download, - CheckSquare, - UserCheck, - FileText, - LucideIcon, - Settings, - Handshake, - BellRing, - BarChart3, -} from "lucide-react"; -import { useUserDetail } from "../hooks/useUserDetail"; -import { useAuthUser } from "@/shared/hooks/useAuthUser"; -import Cookies from "js-cookie"; -import { useDelegations } from "../hooks/useDelegations"; -import Top from "./Top"; - -import { useReport } from "../hooks/useReport"; -import { hasApprovalPermission } from "@/record-management/routes/routes"; -import { useReportByHooks } from "../hooks/useReportByHooks"; -import { useMyCollaborations } from "../hooks/useMyCollaborations"; - -interface HeaderProps { - onToggleSidebar?: () => void; -} - -export interface INavTabs { - icon: LucideIcon; - isVissible?: boolean; - label?: string; - title?: string; - href?: string; - url?: string; - isActive?: boolean; - count?: boolean; - isPrimary?: boolean; - countBadge?: number; - isUrgent?: boolean; -} - -const Header: React.FC = ({ onToggleSidebar }) => { - const baseParams = { - skip: 0, - take: 10, - orderBy: "employeePosition.createdAt:DESC", - }; - const { t } = useTranslation(); - const location = useLocation(); - const { hasDelegated } = useDelegations(baseParams); - const { userDetails, selectedPositionPermissionKeys, selectedPosition } = - useAuthUser(); - const { permissionKeys } = useUserDetail(userDetails); - const useParentCounts = - permissionKeys.includes("can:viewParentPositionRecord") && - new URLSearchParams(location.search).get("view") === "parent"; - const currentPosition = Cookies.get("current-position-id"); - const canViewApprovalTab = hasApprovalPermission( - selectedPositionPermissionKeys, - ); - - // Reuse the auth layer's already-normalized active position instead of - // re-deriving it here. useAuthUser resolves it by matching BOTH `id` and - // `employeePositionId` (and honors the delegated-position cookie), so a - // delegate's position is found and `isDelegate` is reliable. The previous - // local lookup matched `pos.id === selectedPositionId` only — but - // `selectedPositionId` is normalized to `employeePositionId`, so the find - // returned undefined and `!undefined` left every tab visible for delegates. - const canViewDelegationTab = !selectedPosition?.isDelegate; - const userRoles = - userDetails?.roles?.map((r: { key: string }) => r.key) || []; - const showUserManagementShortcut = - userRoles.includes("admin") || - userRoles.includes("unit_admin") || - userRoles.includes("super_admin"); - - const { - dashboard, - ROdashboard, - TotalDraftExternal, - TotalDraftInternal, - TotalDraftCC, - ROTotalDraftIncoming, - ROPending, - TotalUrgentDraftInternal, - TotalUrgentDraftExternal, - ROPendingUrgent, - } = useReport(""); - const { breakdownCounts } = useReportByHooks({ - isSecretary: useParentCounts, - }); - const { data: draftCollaborations } = useMyCollaborations({ - skip: 0, - take: 1, - signStatus: "draft", - }); - const collaborationDraftCount = - draftCollaborations?.count ?? dashboard?.collaborationCount?.draft ?? 0; - const incomingHref = useParentCounts - ? "/record-management/userIncoming?view=parent" - : "/record-management/userIncoming"; - - const navigationTabs = useMemo( - (): INavTabs[] => [ - { - icon: LayoutGrid, - label: t("header.navigation.dashboard"), - href: "/record-management/dashboard", - isActive: location.pathname.includes("/record-management/dashboard"), - isPrimary: true, - // Delegates see only Outgoing, Incoming, Approval — hide Dashboard. - isVissible: canViewDelegationTab, - }, - { - icon: BarChart3, - label: "Reports", - href: "/record-management/sector-reports", - isActive: location.pathname.startsWith( - "/record-management/sector-reports", - ), - isPrimary: false, - isVissible: false, - }, - { - icon: Upload, - label: t("header.navigation.outgoing"), - href: "/record-management/userRecords", - isActive: - location.pathname.startsWith("/record-management/userRecords") && - new URLSearchParams(location.search).get("from") !== "collaborations", - isPrimary: true, - isVissible: true, - }, - { - icon: Download, - label: t("header.navigation.incoming"), - href: incomingHref, - isActive: - location.pathname.startsWith("/record-management/userIncoming") || - location.pathname.startsWith( - "/record-management/viewIncoming/incoming/", - ) || - location.pathname.startsWith( - "/record-management/viewIncoming/internal", - ) || - location.pathname.startsWith("/record-management/viewIncoming/cc"), - isPrimary: true, - isVissible: true, - countBadge: - breakdownCounts.external + - breakdownCounts.externalSmart + - breakdownCounts.internal + - breakdownCounts.ccUnseen + - breakdownCounts.forYourReference, - isUrgent: TotalUrgentDraftInternal > 0 || TotalUrgentDraftExternal > 0, - }, - { - icon: CheckSquare, - label: t("header.navigation.approval"), - href: "/record-management/approval", - isActive: - location.pathname.startsWith("/record-management/approval") || - location.pathname.startsWith("/record-management/viewApproval"), - isPrimary: false, - isVissible: canViewApprovalTab, - countBadge: breakdownCounts?.approval || 0, - isUrgent: - (dashboard?.activeApprovalCount?.myUrgentWorkflowCount || 0) > 0, - }, - { - icon: UserCheck, - label: t("header.navigation.delegation"), - href: "/record-management/delegation", - isActive: location.pathname.startsWith("/record-management/delegation"), - isPrimary: false, - isVissible: canViewDelegationTab, - }, - { - icon: Handshake, - label: t("header.navigation.collaborations"), - href: "/record-management/collaborations", - isActive: - location.pathname.startsWith("/record-management/collaborations") || - new URLSearchParams(location.search).get("from") === "collaborations", - isPrimary: false, - isVissible: canViewDelegationTab, - countBadge: collaborationDraftCount, - }, - { - icon: Settings, - label: t("header.navigation.settings"), - href: "/record-management/uploadTeeterandSignature", - isActive: location.pathname.startsWith( - "/record-management/uploadTeeterandSignature", - ), - isPrimary: false, - isVissible: canViewDelegationTab, - }, - ], - [ - t, - location.pathname, - location.search, - TotalDraftCC, - TotalDraftInternal, - TotalDraftExternal, - TotalUrgentDraftInternal, - TotalUrgentDraftExternal, - canViewApprovalTab, - canViewDelegationTab, - useParentCounts, - incomingHref, - breakdownCounts.approval, - breakdownCounts.ccUnseen, - breakdownCounts.external, - breakdownCounts.externalSmart, - breakdownCounts.forYourReference, - breakdownCounts.internal, - dashboard?.activeApprovalCount?.myNotUrgentWorkflowsCount, - dashboard?.activeApprovalCount?.myUrgentWorkflowCount, - collaborationDraftCount, - ], - ); - - const recordOfficerNavs = useMemo( - (): INavTabs[] => [ - { - icon: LayoutGrid, - label: t("header.navigation.dashboard"), - href: "/record-management/dashboard", - isActive: location.pathname.includes("/dashboard"), - isVissible: true, - }, - { - icon: Upload, - label: t("header.navigation.outgoing"), - href: "/record-management/recordOfficer/outgoing", - isActive: - location.pathname.startsWith( - "/record-management/recordOfficer/outgoing", - ) || - location.pathname.includes("/record-management/view/") || - location.pathname.includes("/record-management/outgoingview/"), - isVissible: true, - }, - { - icon: Download, - label: t("header.navigation.incoming"), - href: "/record-management/recordOfficer/incoming", - isActive: - location.pathname.startsWith( - "/record-management/recordOfficer/incoming", - ) || - location.pathname.includes("/record-management/viewIncoming/") || - location.pathname.includes("/record-management/recordViewIncoming/"), - isVissible: true, - countBadge: ROTotalDraftIncoming, - }, - { - icon: FileText, - label: t("header.navigation.pending"), - href: "/record-management/pending", - isActive: - location.pathname.startsWith("/record-management/pending") || - location.pathname.includes("/record-management/viewPending/"), - isVissible: true, - countBadge: ROPending, - isUrgent: ROPendingUrgent > 0, - }, - ], - [t, location.pathname, ROTotalDraftIncoming, ROPending, ROPendingUrgent], - ); - - const activeNavigationTabs = useMemo(() => { - if (permissionKeys.includes("can:dispatchRecords")) { - return recordOfficerNavs; - } - return navigationTabs.filter((tab) => tab.isVissible); - }, [permissionKeys, navigationTabs, recordOfficerNavs]); - - // const { user } = useAuth(); - // console.log("User Info:", user); - // const organizationId = - // user?.employee && user.employee.length > 0 - // ? user.employee[0].unitId - // : undefined; - - // const unitId = organizationId; - return ( -
- - - {/* Navigation Tabs */} - {currentPosition ? ( -
- {/* Mobile layout: Simple horizontal scrollable (up to sm) */} -
-
- {activeNavigationTabs.map((tab, index) => ( - - cn( - "flex items-center gap-1 px-3 py-2 mr-2 text-xs font-medium transition-colors whitespace-nowrap relative", - isActive - ? "text-primary-800 dark:text-white border-b-2 border-primary-600 dark:border-primary-400" - : "text-gray-700 dark:text-gray-300 hover:text-primary-800 dark:hover:text-white", - ) - } - > - - {tab.label} - - {/* Count Badge - positioned on tab */} - {typeof tab?.countBadge === "number" && - tab.countBadge > 0 && ( - - {tab.countBadge > 99 ? "99+" : tab.countBadge} - - )} - - {/* Urgent Bell - positioned on tab */} - {typeof tab?.isUrgent === "boolean" && tab.isUrgent && ( - - - - - )} - - {/* Live indicator for Delegation */} - {hasDelegated && - (tab.label?.toLowerCase() === "delegation" || - tab.label?.toLowerCase() === "ዉክልና") && ( - - - - - )} - - ))} -
-
- - {/* Desktop layout: All tabs in one row (sm and up) */} -
-
- {activeNavigationTabs.map((tab, index) => ( - - cn( - "flex items-center justify-center px-2.5 py-2.5 rounded-sm text-sm whitespace-nowrap flex-1 font-Urbanist relative", - tab.isActive - ? "bg-primary-50 dark:bg-primary-900/40 text-primary-800 dark:text-white font-medium" - : "text-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-700", - ) - } - > - - - {tab.label} - - {/* Count Badge (inline) */} - {typeof tab?.countBadge === "number" && - tab.countBadge > 0 && ( - - {tab.countBadge > 99 ? "99+" : tab.countBadge} - - )} - - {/* Urgent Bell (pulsing) */} - {typeof tab?.isUrgent === "boolean" && tab.isUrgent && ( - - {/* Pulse effect */} - - {/* Bell icon */} - - - - - )} - - - {/* Live indicator for Delegation */} - {hasDelegated && - (tab.label?.toLowerCase() === "delegation" || - tab.label?.toLowerCase() === "ዉክልና") && ( - - - - - )} - - ))} -
-
-
- ) : null} -
- ); -}; - -export default Header; diff --git a/apps/edr-freight-web/backoffice/src/record-management/components/common/NavigationHeader/Profile.tsx b/apps/edr-freight-web/backoffice/src/record-management/components/common/NavigationHeader/Profile.tsx deleted file mode 100644 index 6d128954a..000000000 --- a/apps/edr-freight-web/backoffice/src/record-management/components/common/NavigationHeader/Profile.tsx +++ /dev/null @@ -1,1049 +0,0 @@ -import React, { useEffect, useState } from "react"; -import { - Card, - CardContent, - CardHeader, - CardTitle, -} from "@/shared/common/ui/card"; -import { Badge } from "@/shared/common/ui/badge"; -import { - ChevronLeft, - Mail, - User, - Briefcase, - Key, - CheckCircle2, - Shield, - Lock, - CircleUser, - Star, - ShieldCheck, - Clock, - UserPen, - ShieldOff, - IdCard, - Settings, - Building, - Users, - FileText, - ChevronDown, - LogOut, - Monitor, -} from "lucide-react"; -import { Button } from "@/shared/common/ui/button"; -import { NavLink, useNavigate } from "react-router-dom"; -import { useAuthUser } from "../../../hooks/useAuthUser"; -import { useTranslation } from "react-i18next"; -import { motion } from "framer-motion"; -import { useLocalizedName } from "@/shared/common/localizedName"; -import Top from "../Top"; -import { Label } from "@/shared/common/ui/label"; -import { Switch } from "@/shared/common/ui/switch"; -import { useSessions } from "@/shared/hooks/useSession"; -import { useQueryClient } from "@tanstack/react-query"; -import Loader from "@/record-management/components/Loader/loader"; - -// Define TypeScript interfaces for the props -interface PositionDetailItemProps { - icon: React.ReactNode; - label: string; - value: string; -} - -interface InfoBoxProps { - icon: React.ReactNode; - label: string; - value: string; - color?: "blue" | "purple" | "green" | "gray"; - fullWidth?: boolean; - amharic?: boolean; - capitalize?: boolean; -} - -interface ProfileInfoItemProps { - icon: React.ReactNode; - label: string; - value: string; - capitalize?: boolean; -} - -interface SecurityItemProps { - title: string; - description: string; - action: React.ReactNode; - status?: "secure" | "warning" | "inactive"; - - twoFactorStatus?: "on" | "off"; -} - -interface ActivityItemProps { - icon: React.ReactNode; - title: string; - time: string; - type: "success" | "info" | "warning"; -} - -const ProfilePage = () => { - const navigate = useNavigate(); - const { t } = useTranslation(); - const queryClient = useQueryClient(); - - const { - userDetails, - isLoading, - isError, - refetch, - logout, - setTwoFactorAuth, - twoFactorData, - editTwoFA, - isLoadingStatus, - } = useAuthUser(); - const userRoles = userDetails?.roles?.map((r: { key: string }) => r.key) || []; - const showUserManagementShortcut = - userRoles.includes("admin") || - userRoles.includes("unit_admin") || - userRoles.includes("super_admin"); - const localizedName = useLocalizedName(); - const [isSessionsExpanded, setIsSessionsExpanded] = useState(false); - const [twoFactor, setTwoFactor] = useState(false); - const [permissionsExpanded, setPermissionsExpanded] = useState(false); - const PERMISSIONS_INITIAL_SHOW = 4; - useEffect(() => { - if (!userDetails) return; - if (twoFactorData?.[0]) { - setTwoFactor(twoFactorData?.[0].isMFARequired); - } - }, [twoFactorData, userDetails]); - - const { - data: sessionsQuery, - deleteSession, - deleteAllSessions, - } = useSessions({ - skip: 0, - take: 10, - orderBy: "CreatedAt:DESC", - }); - const userSessions = sessionsQuery?.sessions || []; - const handleLogoutSession = (sessionId: string) => { - deleteSession(sessionId); - queryClient.invalidateQueries({ queryKey: ["my-sessions"] }); - }; - const handleLogoutAllSessions = () => { - const otherSessions = userSessions.filter((s) => s.id); - - if (otherSessions.length > 0) { - deleteAllSessions({ sessionIds: otherSessions.map((s) => s.id) }); - queryClient.invalidateQueries({ queryKey: ["my-sessions"] }); - } - }; - const handleFactorAuthentication = () => { - if (isLoading || isLoadingStatus) return; // prevent duplicate clicks - if (!userDetails) return; - - const item = twoFactorData?.[0]; - const hasExistingRecord = !!item?.id; - if (!hasExistingRecord) { - // ✅ POST only if no record exists at all - setTwoFactorAuth({ isMFARequired: true }); - } else { - // Toggle using PUT/edit if a record already exists - const newStatus = !twoFactor; - editTwoFA({ id: item.id, isEnabled: newStatus }); - } - }; - - const handleLogout = () => { - logout(); - }; - - if (isLoading) { - return ; - } - - if (isError || !userDetails) { - return ( -
- -
- - - -

- {t("profile.error")} -

-
- - - -
-
- ); - } - - const employeeData = userDetails.employee?.[0] || {}; - const position = employeeData.positions?.[0] || {}; - const positionIsDeletation = employeeData.positions.filter( - (item: any) => item.isDelegate - ); - const positionHasDeletation = employeeData.positions.filter( - (item: any) => item.hasDelegated - ); - // Animation variants - const container = { - hidden: { opacity: 0 }, - show: { - opacity: 1, - transition: { - staggerChildren: 0.1, - }, - }, - }; - - const item = { - hidden: { opacity: 0, y: 20 }, - show: { opacity: 1, y: 0 }, - }; - - // New reusable components for the improved layout - const PositionDetailItem: React.FC = ({ - icon, - label, - value, - }) => ( - -
- {icon} -
-
-

- {label} -

-

- {value} -

-
-
- ); - - const InfoBox: React.FC = ({ - icon, - label, - value, - color = "gray", - fullWidth = false, - amharic = false, - capitalize = false, - }) => ( - -
-
- {icon} -
-

- {label} -

-
-

- {value || "Not specified"} -

-
- ); - - // Reusable Component: ProfileInfoItem - const ProfileInfoItem: React.FC = ({ - icon, - label, - value, - capitalize = false, - }) => ( - -
- {icon} -
-
-

{label}

-

- {value} -

-
-
- ); - - // Reusable Component: SecurityItem - const SecurityItem: React.FC = ({ - title, - description, - status, - action, - twoFactorStatus, - }) => ( - -
-
-
-

- {title} -

-

- {description} -

-
-
- {action} -
- ); - - // Reusable Component: ActivityItem - const ActivityItem: React.FC = ({ - icon, - title, - time, - type, - }) => ( - -
- {icon} -
-
-

{title}

-

{time}

-
-
- ); - - return ( -
- -
- {/* Enhanced Header */} - -
- - - - -
- - -
- - {/* User Quick Stats */} - -
-
- - Online - -
-
- - {t(`profile.userTypes.${userDetails.userType}`)} - -
-
- - {/* Main Content Grid - Reorganized */} - - {/* Left Sidebar - Profile & Account Status */} -
- {/* Enhanced Profile Card */} - - - {/* Profile Header with Gradient */} -
-
-
- - {userDetails.status === "accepted" - ? "Verified" - : "Pending"} - -
- - {/* Profile Avatar */} -
- -
- -
-
-
-
-
- - - {/* User Info */} -
- - {localizedName(userDetails.name) || t("profile.noName")} - -
- - @{userDetails.username} -
-
- - {/* Status Indicators */} -
- -
- {positionIsDeletation ? "Yes" : "No"} -
-
Delegate
-
- -
- {position.permissions?.length || 0} -
-
Permissions
-
-
- - {/* Profile Details */} -
- } - label={t("profile.email")} - value={userDetails.email} - /> - } - label={t("profile.userType")} - value={t(`profile.userTypes.${userDetails.userType}`)} - capitalize - /> - } - label={t("profile.passwordSet")} - value={ - userDetails.hasSetPassword - ? t("common.yes") - : t("common.no") - } - /> -
- - {/* Action Buttons */} -
- - - - - - -
-
-
-
- - {/* Account Status Card */} - - - - - - Account Status - - - - {/* Verification Status */} -
-
-
- {userDetails.status === "accepted" ? ( - - ) : ( - - )} -
-
-

- Account Status -

-

- {userDetails.status === "accepted" - ? "Verified and active" - : "Pending approval"} -

-
-
- - {userDetails.status === "accepted" ? "Active" : "Pending"} - -
- - {/* Password Status */} -
-
-
- -
-
-

- Password -

-

- {userDetails.hasSetPassword - ? "Secured with password" - : "No password set"} -

-
-
- - {userDetails.hasSetPassword ? "Set" : "Not Set"} - -
- - {/* Quick Action */} -
- - - -
-
-
-
-
- - {/* Right Column - Main Content */} -
- - - - -
- -
-
- Employee Information -

- Personal & organizational details -

-
-
-
- -
- {/* Basic Info */} -
- } - label="Employee ID" - value={employeeData?.id || "Not specified"} - color="blue" - /> - } - label="Organization ID" - value={employeeData?.organizationId || "Not specified"} - color="purple" - /> - } - label="User Type" - value={userDetails.userType} - color="green" - capitalize - /> -
- - {/* Names */} -
- } - label="English Name" - value={employeeData?.name?.en || "Not specified"} - fullWidth - /> - } - label="Amharic Name" - value={employeeData?.name?.am || "Not specified"} - fullWidth - amharic - /> -
- - {/* Position Summary */} - {userDetails.employee[0]?.positions?.[0] && ( -
-

- - Current Position -

-
-
-

- Position -

-

- {userDetails.employee[0].positions[0].name?.en || - "Not specified"} -

-
-
-

- Delegation -

- - {positionHasDeletation.length ? "Active" : "None"} - -
-
-
- )} -
-
-
-
- {/* Top Row: Position Details & Security */} -
- {/* Position Details */} - - - - -
- -
-
- {t("profile.positionDetails")} -

- Your current role -

-
-
-
- -
- } - label={t("profile.positionName")} - value={localizedName(position.name) || t("common.na")} - /> - } - label={t("profile.positionKey")} - value={position.key || t("common.na")} - /> - } - label={t("profile.employeeId")} - value={employeeData.id || t("common.na")} - /> -
-
-
-
- - {/* Security Card */} - - - - -
- -
-
- {t("profile.accountSecurity")} -

- Account safety -

-
-
-
- -
- - - - } - /> - - - -
- } - /> -
-
-
- - - {t("profile.sessions")} - - {userSessions.length > 0 && ( - - {userSessions.length} active - - )} -
- -
- - {/* Expandable Sessions Content */} - {isSessionsExpanded && ( - -
- {userSessions.length === 0 ? ( -
- -

No active sessions

-
- ) : ( - <> - {userSessions.map((session) => ( -
-
-
- -
-
-

- Device: {session.device} -

-

- Email: {session.email} -

-

- Created:{" "} - {new Date( - session.createdAt - ).toLocaleString()} -

-
-
- -
- ))} - - - - )} -
-
- )} -
-
- - - -
- - {/* Middle Row: Permissions */} - - - - -
- -
-
- {t("profile.permissions")} -

- Access rights and privileges -

-
-
-
- - {position.permissions?.length > 0 ? ( - - {(permissionsExpanded ? position.permissions : position.permissions.slice(0, PERMISSIONS_INITIAL_SHOW)).map((perm: any, index: number) => ( - -
- -
- - {perm.key} - -
- ))} - {position.permissions.length > PERMISSIONS_INITIAL_SHOW && ( - - )} -
- ) : ( -
-
- -
-

- {t("profile.noPermissions")} -

-

- No special access rights assigned -

-
- )} -
-
-
-
- -
- - ); -}; - -export default ProfilePage; diff --git a/apps/edr-freight-web/backoffice/src/record-management/components/common/Top.tsx b/apps/edr-freight-web/backoffice/src/record-management/components/common/Top.tsx index 71afbee2d..82e716d66 100644 --- a/apps/edr-freight-web/backoffice/src/record-management/components/common/Top.tsx +++ b/apps/edr-freight-web/backoffice/src/record-management/components/common/Top.tsx @@ -17,7 +17,6 @@ import { ClipboardList, Clock, FileText, - Home, Languages, Key, LogOut, @@ -256,10 +255,28 @@ const Top: React.FC = ({ }; return ( -
-
-
-
+
+
+
+ {onToggleSidebar ? ( + + + + + + Toggle sidebar + + + ) : ( @@ -320,191 +337,183 @@ const Top: React.FC = ({ ))} + )} -
- -
- - {showUserManagementShortcut && ( - - )} +
+
-
+ {showUserManagementShortcut && ( + + )} +
+ +
+ + + {canActivateUsers && ( +
+ + + {pendingUsersCount > 0 && ( + + {pendingUsersCount > 99 ? "99+" : pendingUsersCount} + + )} + + {openPendingUsers && ( +
+ +
+ )} +
+ )} + +
- {canActivateUsers && ( -
- - - {pendingUsersCount > 0 && ( - - {pendingUsersCount > 99 ? "99+" : pendingUsersCount} - - )} - - {openPendingUsers && ( -
- -
- )} + {openNotifications && ( +
+
)} +
-
- + {openReminders && ( +
+ +
+ )} +
+ + + + + + + + {UI_LANGUAGE_OPTIONS.map((lang) => ( + changeLanguage(lang.value)} className={cn( - "h-4 w-4 transition-colors", - openNotifications && - "fill-primary-100 text-primary-700 dark:fill-primary-900/40 dark:text-primary-300", + "flex cursor-pointer items-center justify-between rounded-lg px-3 py-2.5 text-sm text-gray-700 transition-colors hover:bg-primary-50 dark:text-gray-200 dark:hover:bg-primary-900/30", + currentLanguage === lang.value && + "bg-primary-100 text-primary-800 dark:bg-primary-800/50 dark:text-white", )} - /> - {unseenCount > 0 && ( - - {unseenCount > 99 ? "99+" : unseenCount} - - )} - + > +
+ + {getUiLanguageShortLabel(lang.value, t)} + + {getUiLanguageLabel(lang.value, t)} +
+ {currentLanguage === lang.value && ( + + )} +
+ ))} +
+
- {openNotifications && ( -
- -
- )} -
- - {/* Reminders */} -
+ + - {openReminders && ( -
- -
- )} -
- - - - - - - - {UI_LANGUAGE_OPTIONS.map((lang) => ( - changeLanguage(lang.value)} - className={cn( - "flex cursor-pointer items-center justify-between rounded-lg px-3 py-2.5 text-sm text-gray-700 transition-colors hover:bg-primary-50 dark:text-gray-200 dark:hover:bg-primary-900/30", - currentLanguage === lang.value && - "bg-primary-100 text-primary-800 dark:bg-primary-800/50 dark:text-white", - )} - > -
- - {getUiLanguageShortLabel(lang.value, t)} - - {getUiLanguageLabel(lang.value, t)} -
- {currentLanguage === lang.value && ( - - )} -
- ))} -
-
- - - - - - - -
-

{fullName}

-

- {roleLabel} -

+ > + {initials.toUpperCase()}
+
+ + {fullName} + + + {roleLabel} + +
+ + + + +
+

{fullName}

+

+ {roleLabel} +

+
+ + navigate("/profile")} + > + + {t("header.viewProfile")} + + + navigate("/update-profile")} + > + + {t("header.editProfile")} + + + navigate("/change-password")} + > + + {t("header.changePassword")} + + + {showRecordManagementShortcut && ( navigate("/profile")} + onClick={() => navigate("/record-management/dashboard")} > - - {t("header.viewProfile")} + + {t("nav.Record Management")} + )} + {showUserManagementShortcut && ( navigate("/update-profile")} + onClick={() => navigate("/user-management")} > - - {t("header.editProfile")} + + + {t("dashboard.userManagement", "User Management")} + + )} - navigate("/change-password")} - > - - {t("header.changePassword")} - + - {showRecordManagementShortcut && ( - navigate("/record-management/dashboard")} - > - - {t("nav.Record Management")} - - )} - - {showUserManagementShortcut && ( - navigate("/user-management")} - > - - - {t("dashboard.userManagement", "User Management")} - - - )} - - - - - - {t("header.signOut")} - -
-
-
+ + + {t("header.signOut")} + + +
-
-
+
+
); }; diff --git a/apps/edr-freight-web/backoffice/src/user-management/Applayout.tsx b/apps/edr-freight-web/backoffice/src/user-management/Applayout.tsx index 25f8700e8..ade96250d 100644 --- a/apps/edr-freight-web/backoffice/src/user-management/Applayout.tsx +++ b/apps/edr-freight-web/backoffice/src/user-management/Applayout.tsx @@ -1,84 +1,92 @@ -import { Link, Outlet, useLocation } from "react-router-dom"; -import { AppMenuTabs } from "./AppMenuTabs"; -import { - Sidebar, - SidebarContent, - SidebarHeader, - SidebarInset, - SidebarRail, - SidebarTrigger, -} from "@/shared/common/ui/sidebar"; -import Top from "@/record-management/components/common/Top"; -import { useAuth } from "@/shared/context/AuthContext"; - -export const AppLayout = () => { - const { pathname } = useLocation(); - const isAuthPage = pathname === "/"; - const { user } = useAuth(); - - const userRoles = user?.roles?.map((role) => role.key) || []; - const isSuperAdmin = userRoles.includes("super_admin"); - - // Standalone full-page scroll container for the auth screen — the host - // chrome is `overflow: hidden`, so this subtree must scroll itself. - if (isAuthPage) { - return ( -
- -
- ); - } - - return ( - <> - {/* is a shared component rendered as a full-width `fixed` 64px bar, - so the sidebar is offset to start beneath it (top-16) rather than - owning the top-left corner. Collapses to an icon rail on desktop; on - mobile it's a Sheet drawer opened by the below (Top's - own burger is a module-nav dropdown, not the sidebar toggle). */} - - - - EDR -
- - User Management - - - EDR Freight - -
- -
- - - - -
- - - - {/* Top provides its own in-flow h-24 spacer clearing the fixed header. */} - {/* Mobile-only drawer opener — desktop shows the sidebar/rail directly. */} -
- - - Menu - -
-
-
- -
-
-
- - ); -}; +import { Link, Outlet, useLocation } from "react-router-dom"; +import { ArrowLeft } from "lucide-react"; +import { AppMenuTabs } from "./AppMenuTabs"; +import { + Sidebar, + SidebarContent, + SidebarHeader, + SidebarInset, + SidebarRail, + useSidebar, +} from "@/shared/common/ui/sidebar"; +import Top from "@/record-management/components/common/Top"; +import { useAuth } from "@/shared/context/AuthContext"; + +export const AppLayout = () => { + const { pathname } = useLocation(); + const isAuthPage = pathname === "/"; + const { toggleSidebar } = useSidebar(); + const { user } = useAuth(); + + const userRoles = user?.roles?.map((role) => role.key) || []; + const isSuperAdmin = userRoles.includes("super_admin"); + + // Standalone full-page scroll container for the auth screen — the host + // chrome is `overflow: hidden`, so this subtree must scroll itself. + if (isAuthPage) { + return ( +
+ +
+ ); + } + + return ( + <> + {/* Full-height sidebar owning the left column (back-to-home + brand at the + top). lives inside , to the right of the sidebar, + and is `sticky` — so it never overlaps the sidebar and re-flows when + the sidebar collapses to its icon rail. Top's burger (onToggleSidebar) + drives collapse on desktop and the Sheet drawer on mobile. */} + + + + + + Back to home + + + + EDR +
+ + User Management + + + EDR Freight + +
+ +
+ + + + +
+ + {/* Internal scroll container: the sidebar stays fixed and full-height + while this column scrolls beneath the sticky bar. */} + + +
+
+ +
+
+
+ + ); +}; diff --git a/apps/edr-freight-web/backoffice/src/user-management/components/position-management/PositionLists.tsx b/apps/edr-freight-web/backoffice/src/user-management/components/position-management/PositionLists.tsx index 9af344caf..d15d82799 100644 --- a/apps/edr-freight-web/backoffice/src/user-management/components/position-management/PositionLists.tsx +++ b/apps/edr-freight-web/backoffice/src/user-management/components/position-management/PositionLists.tsx @@ -210,7 +210,7 @@ export default function PositionManagement() { return (
- + {t("contentManagement.permissionType")} diff --git a/apps/edr-freight-web/backoffice/src/user-management/pages/position-management/index.tsx b/apps/edr-freight-web/backoffice/src/user-management/pages/position-management/index.tsx index 57d7a1898..a052caa11 100644 --- a/apps/edr-freight-web/backoffice/src/user-management/pages/position-management/index.tsx +++ b/apps/edr-freight-web/backoffice/src/user-management/pages/position-management/index.tsx @@ -1,13 +1,7 @@ import PositionManagement from "@/user-management/components/position-management/PositionLists"; -import { t } from "i18next"; const PositionManagementPage = () => { - return ( -
-

{t("contentManagement.permissionManagement")}

- -
- ); + return ; }; export default PositionManagementPage;