• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

alkem-io / client-web / #5937

12 Oct 2023 06:39AM UTC coverage: 6.211%. First build
#5937

Pull #4941

travis-ci

Pull Request #4941: New Navigation: Alkemio menu and User menu

173 of 8291 branches covered (0.0%)

Branch coverage included in aggregate %.

91 of 91 new or added lines in 12 files covered. (100.0%)

1296 of 15362 relevant lines covered (8.44%)

0.41 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/main/ui/platformNavigation/PlatformNavigationUserMenu.tsx
1
import { PropsWithChildren, ReactNode, useState, Suspense } from 'react';
2
import { Box, Divider, MenuList, Typography } from '@mui/material';
3
import { BlockTitle, Caption } from '@/core/ui/typography';
4
import { gutters } from '@/core/ui/grid/utils';
5
import { buildLoginUrl, buildUserAccountUrl } from '@/main/routing/urlBuilders';
6
import {
7
  AssignmentIndOutlined,
8
  DashboardOutlined,
9
  ExitToAppOutlined,
10
  HdrStrongOutlined,
11
  LanguageOutlined,
12
  MeetingRoomOutlined,
13
} from '@mui/icons-material';
14
import SettingsIcon from '@mui/icons-material/SettingsOutlined';
15
import { AUTH_LOGOUT_PATH } from '@/core/auth/authentication/constants/authentication.constants';
16
import { useTranslation } from 'react-i18next';
17
import { AuthorizationPrivilege, RoleName } from '@/core/apollo/generated/graphql-schema';
18
import { useCurrentUserContext } from '@/domain/community/userCurrent/useCurrentUserContext';
19
import Gutters from '@/core/ui/grid/Gutters';
20
import { ROUTE_HOME } from '@/domain/platform/routes/constants';
21
import LanguageSelect from '@/core/ui/language/LanguageSelect';
22
import HelpOutlineIcon from '@mui/icons-material/HelpOutline';
23
import { PLATFORM_NAVIGATION_MENU_Z_INDEX } from './constants';
24
import { useLocation } from 'react-router-dom';
25
import NavigatableMenuItem from '@/core/ui/menu/NavigatableMenuItem';
26
import GlobalMenuSurface from '@/core/ui/menu/GlobalMenuSurface';
27
import usePlatformOrigin from '@/domain/platform/routes/usePlatformOrigin';
28
import Avatar from '@/core/ui/avatar/Avatar';
29
import {
30
  PendingMembershipsDialogType,
31
  usePendingMembershipsDialog,
32
} from '@/domain/community/pendingMembership/PendingMembershipsDialogContext';
33
import { usePendingInvitationsCount } from '@/domain/community/pendingMembership/usePendingInvitationsCount';
34
import FocusTrap from '@mui/material/Unstable_TrapFocus';
35
import LocalOfferOutlinedIcon from '@mui/icons-material/LocalOfferOutlined';
36
import { lazyWithGlobalErrorHandler } from '@/core/lazyLoading/lazyWithGlobalErrorHandler';
37

38
const PendingMembershipsDialog = lazyWithGlobalErrorHandler(
×
39
  () => import('@/domain/community/pendingMembership/PendingMembershipsDialog')
40
);
41
const HelpDialog = lazyWithGlobalErrorHandler(() => import('@/core/help/dialog/HelpDialog'));
42

43
interface PlatformNavigationUserMenuProps {
44
  surface: boolean;
×
45
  footer?: ReactNode;
46
  onClose?: () => void;
×
47
}
48

49
export const UserMenuDivider = () => <Divider sx={{ width: '85%', marginX: 'auto' }} />;
50

51
const PlatformNavigationUserMenu = ({
52
  ref,
53
  surface,
54
  onClose,
55
  footer,
56
  children,
57
}: PropsWithChildren<PlatformNavigationUserMenuProps> & {
58
  ref?: React.Ref<HTMLDivElement>;
×
59
}) => {
×
60
  const { t } = useTranslation();
61

×
62
  const { pathname, search } = useLocation();
63

×
64
  const platformOrigin = usePlatformOrigin();
65
  const homeUrl = platformOrigin && `${platformOrigin}${ROUTE_HOME}`;
×
66

67
  const [isHelpDialogOpen, setIsHelpDialogOpen] = useState(false);
×
68
  const { setOpenDialog } = usePendingMembershipsDialog();
×
69

70
  const { userModel, platformPrivilegeWrapper: userWrapper, isAuthenticated, platformRoles } = useCurrentUserContext();
×
71

72
  const isAdmin = userWrapper?.hasPlatformPrivilege?.(AuthorizationPrivilege.PlatformAdmin);
73

74
  const { count: pendingInvitationsCount } = usePendingInvitationsCount();
×
75

76
  // the roles should follow the order
77
  const getRole = (): string | null => {
×
78
    for (const platformRole of platformRoles) {
79
      switch (platformRole) {
80
        case RoleName.GlobalAdmin:
81
          return t('common.roles.GLOBAL_ADMIN');
×
82
        case RoleName.GlobalSupport:
83
          return t('common.roles.GLOBAL_SUPPORT');
84
        case RoleName.GlobalLicenseManager:
85
          return t('common.roles.GLOBAL_LICENSE_MANAGER');
86
        case RoleName.PlatformBetaTester:
87
          return t('common.roles.PLATFORM_BETA_TESTER');
88
        case RoleName.PlatformVcCampaign:
89
          return t('common.roles.PLATFORM_VC_CAMPAIGN');
90
      }
91
    }
92
    return null;
×
93
  };
94
  const role = getRole();
95

96
  const Wrapper = surface ? GlobalMenuSurface : Box;
97

98
  return (
99
    <>
100
      <Wrapper ref={ref}>
101
        {userModel && (
102
          <Gutters disableGap alignItems="center" sx={{ paddingBottom: 1 }}>
103
            <Avatar
×
104
              size="large"
105
              src={userModel.profile.avatar?.uri}
106
              alt={
×
107
                userModel.profile?.displayName
×
108
                  ? t('common.avatar-of', { user: userModel.profile?.displayName })
109
                  : t('common.avatar')
110
              }
111
            />
112
            <BlockTitle lineHeight={gutters(2)}>{userModel.profile.displayName}</BlockTitle>
113
            {role && (
114
              <Caption color="neutralMedium.main" textTransform="uppercase">
115
                {role}
×
116
              </Caption>
117
            )}
118
          </Gutters>
119
        )}
120
        <FocusTrap open>
121
          <MenuList autoFocus disablePadding sx={{ paddingY: 1, outline: 'none' }}>
122
            {!isAuthenticated && (
123
              <NavigatableMenuItem
124
                iconComponent={MeetingRoomOutlined}
125
                route={buildLoginUrl(pathname, search)}
126
                onClick={onClose}
127
              >
128
                <Typography variant="inherit" fontWeight="bold">
129
                  {t('topBar.sign-in')}
130
                </Typography>
131
              </NavigatableMenuItem>
×
132
            )}
133
            <NavigatableMenuItem iconComponent={DashboardOutlined} route={homeUrl} onClick={onClose}>
×
134
              {t('pages.home.title')}
135
            </NavigatableMenuItem>
136
            {userModel && (
137
              <NavigatableMenuItem
138
                iconComponent={AssignmentIndOutlined}
139
                route={userModel.profile.url}
140
                onClick={onClose}
141
              >
142
                {t('pages.user-profile.title')}
×
143
              </NavigatableMenuItem>
×
144
            )}
145
            {userModel && (
146
              <NavigatableMenuItem
147
                iconComponent={LocalOfferOutlinedIcon}
148
                route={buildUserAccountUrl(userModel.profile.url)}
149
                onClick={onClose}
150
              >
151
                {t('pages.home.mainNavigation.myAccount')}
152
              </NavigatableMenuItem>
153
            )}
154
            {userModel && (
155
              <NavigatableMenuItem
156
                iconComponent={HdrStrongOutlined}
157
                onClick={() => {
×
158
                  setOpenDialog({ type: PendingMembershipsDialogType.PendingMembershipsList });
159
                  onClose?.();
160
                }}
161
              >
162
                {t('community.pendingMembership.pendingMembershipsWithCount', { count: pendingInvitationsCount })}
163
              </NavigatableMenuItem>
164
            )}
165
            <UserMenuDivider />
166
            {children}
167
            {isAdmin && (
168
              <NavigatableMenuItem iconComponent={SettingsIcon} route="/admin" onClick={onClose}>
169
                {t('common.administration')}
170
              </NavigatableMenuItem>
171
            )}
172
            <LanguageSelect
173
              anchorOrigin={{
174
                vertical: 'bottom',
175
                horizontal: 'left',
176
              }}
177
              transformOrigin={{
178
                vertical: 'top',
179
                horizontal: 'left',
180
              }}
181
              zIndex={PLATFORM_NAVIGATION_MENU_Z_INDEX + 1}
182
            >
183
              {({ openSelect, isOpen }) => (
184
                <NavigatableMenuItem
185
                  id="language-button"
186
                  iconComponent={LanguageOutlined}
187
                  onClick={event => openSelect(event.currentTarget as HTMLElement)}
188
                  aria-controls={isOpen ? 'language-menu' : undefined}
189
                  aria-haspopup="true"
190
                  aria-expanded={isOpen ? 'true' : undefined}
191
                >
192
                  {t('buttons.changeLanguage')}
193
                </NavigatableMenuItem>
194
              )}
195
            </LanguageSelect>
196
            <NavigatableMenuItem
197
              iconComponent={HelpOutlineIcon}
198
              onClick={() => {
199
                setIsHelpDialogOpen(true);
200
                onClose?.();
201
              }}
202
            >
203
              {t('buttons.getHelp')}
204
            </NavigatableMenuItem>
205
            {isAuthenticated && (
206
              <NavigatableMenuItem iconComponent={MeetingRoomOutlined} route={AUTH_LOGOUT_PATH} onClick={onClose}>
207
                {t('buttons.sign-out')}
208
              </NavigatableMenuItem>
209
            )}
210
            <NavigatableMenuItem tabOnly iconComponent={ExitToAppOutlined} onClick={onClose}>
211
              {t('components.navigation.exitMenu')}
212
            </NavigatableMenuItem>
213
            {footer}
214
          </MenuList>
215
        </FocusTrap>
216
      </Wrapper>
217
      {userModel && (
218
        <Suspense fallback={null}>
219
          <PendingMembershipsDialog />
220
        </Suspense>
221
      )}
222
      <Suspense fallback={null}>
223
        <HelpDialog open={isHelpDialogOpen} onClose={() => setIsHelpDialogOpen(false)} />
224
      </Suspense>
225
    </>
226
  );
227
};
228

229
export default PlatformNavigationUserMenu;
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc