diff --git a/src/core/apollo/generated/apollo-hooks.ts b/src/core/apollo/generated/apollo-hooks.ts index 0d06a49490..da66367765 100644 --- a/src/core/apollo/generated/apollo-hooks.ts +++ b/src/core/apollo/generated/apollo-hooks.ts @@ -18056,6 +18056,8 @@ export function refetchSubspacePageQuery(variables: SchemaTypes.SubspacePageQuer export const PlatformLevelAuthorizationDocument = gql` query PlatformLevelAuthorization { platform { + id + myRoles authorization { ...MyPrivileges } @@ -22270,16 +22272,13 @@ export function refetchInnovationLibraryQuery(variables?: SchemaTypes.Innovation export const CampaignBlockCredentialsDocument = gql` query CampaignBlockCredentials { + platform { + id + myRoles + } me { user { id - agent { - id - credentials { - resourceID - type - } - } account { id license { diff --git a/src/core/apollo/generated/graphql-schema.ts b/src/core/apollo/generated/graphql-schema.ts index b4114ecc96..d13bd901e8 100644 --- a/src/core/apollo/generated/graphql-schema.ts +++ b/src/core/apollo/generated/graphql-schema.ts @@ -23536,6 +23536,8 @@ export type PlatformLevelAuthorizationQuery = { __typename?: 'Query'; platform: { __typename?: 'Platform'; + id: string; + myRoles: Array; authorization?: | { __typename?: 'Authorization'; myPrivileges?: Array | undefined } | undefined; @@ -28784,17 +28786,13 @@ export type CampaignBlockCredentialsQueryVariables = Exact<{ [key: string]: neve export type CampaignBlockCredentialsQuery = { __typename?: 'Query'; + platform: { __typename?: 'Platform'; id: string; myRoles: Array }; me: { __typename?: 'MeQueryResults'; user?: | { __typename?: 'User'; id: string; - agent: { - __typename?: 'Agent'; - id: string; - credentials?: Array<{ __typename?: 'Credential'; resourceID: string; type: CredentialType }> | undefined; - }; account?: | { __typename?: 'Account'; diff --git a/src/core/i18n/en/translation.en.json b/src/core/i18n/en/translation.en.json index 75eeb62646..4984f91611 100644 --- a/src/core/i18n/en/translation.en.json +++ b/src/core/i18n/en/translation.en.json @@ -577,6 +577,13 @@ "year": "{{count}} year", "year_plural": "{{count}} years" } + }, + "roles": { + "GLOBAL_ADMIN": "Global Admin", + "SUPPORT": "Support Admin", + "LICENSE_MANAGER": "License Manager", + "BETA_TESTER": "Beta Tester", + "VC_CAMPAIGN": "VC Early Access" } }, "community": { @@ -585,7 +592,7 @@ "memberOrganizations": "Member Organizations ({{count}})", "pendingApplications": "Pending applications", "pendingMemberships": "Pending applications & invitations", - "invitationSent": "Invitation sent succesfully", + "invitationSent": "Invitation sent successfully", "leads": "Leads", "members": "Members", "host": "Host", diff --git a/src/core/ui/icon/BadgeLabel.tsx b/src/core/ui/icon/BadgeLabel.tsx new file mode 100644 index 0000000000..386d3d0b55 --- /dev/null +++ b/src/core/ui/icon/BadgeLabel.tsx @@ -0,0 +1,44 @@ +import { TypographyProps, useTheme } from '@mui/material'; +import { gutters } from '../grid/utils'; +import { Caption } from '../typography'; + +interface BadgeLabelProps extends TypographyProps { + count?: string; + size?: 'small' | 'medium'; +} + +// copy of BadgeCounter but supporting auto width text +const BadgeSizes: Record, TypographyProps> = { + small: { + fontSize: '0.7rem', + lineHeight: gutters(0.8), + width: 'auto', + height: gutters(0.8), + }, + medium: { + width: 'auto', + height: gutters(1), + }, +}; + +const BadgeLabel = ({ count, size = 'medium', ...props }: BadgeLabelProps) => { + const theme = useTheme(); + + return ( + + {count} + + ); +}; + +export default BadgeLabel; diff --git a/src/core/ui/navigation/NavigationBar.tsx b/src/core/ui/navigation/NavigationBar.tsx index 6cdffde879..ee6c665048 100644 --- a/src/core/ui/navigation/NavigationBar.tsx +++ b/src/core/ui/navigation/NavigationBar.tsx @@ -25,6 +25,7 @@ const NavigationBarContent = ({ transparent, children }: PropsWithChildren alpha(theme.palette.primary.main, transparent ? 0 : 0.25), backdropFilter: transparent ? 'none' : 'blur(8px)', position: 'relative', + overflow: 'visible', height: gutters(NAVIGATION_CONTAINER_HEIGHT_GUTTERS - 1), maxWidth: gutters(MAX_CONTENT_WIDTH_GUTTERS - 2), }} diff --git a/src/domain/community/user/providers/UserProvider/UserProvider.tsx b/src/domain/community/user/providers/UserProvider/UserProvider.tsx index 2b4c9d8d12..9ea3ef01cc 100644 --- a/src/domain/community/user/providers/UserProvider/UserProvider.tsx +++ b/src/domain/community/user/providers/UserProvider/UserProvider.tsx @@ -6,7 +6,12 @@ import { useUserProviderQuery, } from '@/core/apollo/generated/apollo-hooks'; import { ErrorPage } from '@/core/pages/Errors/ErrorPage'; -import { AuthorizationPrivilege, LicenseEntitlementType, User } from '@/core/apollo/generated/graphql-schema'; +import { + AuthorizationPrivilege, + LicenseEntitlementType, + PlatformRole, + User, +} from '@/core/apollo/generated/graphql-schema'; import { useAuthenticationContext } from '@/core/auth/authentication/hooks/useAuthenticationContext'; import { toUserMetadata, UserMetadata } from '../../hooks/useUserMetadataWrapper'; @@ -17,6 +22,7 @@ export interface UserContextValue { loadingMe: boolean; // Loading Authentication and Profile data. Once it's false that's enough for showing the page header and avatar. verified: boolean; isAuthenticated: boolean; + platformRoles: PlatformRole[]; accountPrivileges: AuthorizationPrivilege[]; accountEntitlements: LicenseEntitlementType[]; } @@ -28,6 +34,7 @@ const UserContext = createContext({ loadingMe: true, verified: false, isAuthenticated: false, + platformRoles: [], accountPrivileges: [], accountEntitlements: [], }); @@ -75,10 +82,19 @@ const UserProvider: FC = ({ children }) => { loadingMe: loadingMeAndParentQueries, verified, isAuthenticated, + platformRoles: platformLevelAuthorizationData?.platform.myRoles ?? [], accountPrivileges: meData?.me.user?.account?.authorization?.myPrivileges ?? [], accountEntitlements: meData?.me.user?.account?.license?.myLicensePrivileges ?? [], }), - [userMetadata, loading, loadingMeAndParentQueries, verified, isAuthenticated] + [ + userMetadata, + loading, + loadingMeAndParentQueries, + verified, + isAuthenticated, + platformLevelAuthorizationData, + meData, + ] ); if (error) return ; diff --git a/src/domain/platform/PlatformLevelAuthorization.graphql b/src/domain/platform/PlatformLevelAuthorization.graphql index 67c0e11e2e..16e11b3da6 100644 --- a/src/domain/platform/PlatformLevelAuthorization.graphql +++ b/src/domain/platform/PlatformLevelAuthorization.graphql @@ -1,5 +1,7 @@ query PlatformLevelAuthorization { platform { + id + myRoles authorization { ...MyPrivileges } diff --git a/src/main/topLevelPages/myDashboard/Campaigns/CampaignBlock.graphql b/src/main/topLevelPages/myDashboard/Campaigns/CampaignBlock.graphql index d25cb5668e..b9b68c9133 100644 --- a/src/main/topLevelPages/myDashboard/Campaigns/CampaignBlock.graphql +++ b/src/main/topLevelPages/myDashboard/Campaigns/CampaignBlock.graphql @@ -1,14 +1,11 @@ query CampaignBlockCredentials { + platform { + id + myRoles + } me { user { id - agent { - id - credentials { - resourceID - type - } - } account { id license { diff --git a/src/main/topLevelPages/myDashboard/Campaigns/CampaignBlock.tsx b/src/main/topLevelPages/myDashboard/Campaigns/CampaignBlock.tsx index 247a8088da..95ddecd369 100644 --- a/src/main/topLevelPages/myDashboard/Campaigns/CampaignBlock.tsx +++ b/src/main/topLevelPages/myDashboard/Campaigns/CampaignBlock.tsx @@ -1,5 +1,5 @@ import { useCampaignBlockCredentialsQuery } from '@/core/apollo/generated/apollo-hooks'; -import { CredentialType, LicenseEntitlementType } from '@/core/apollo/generated/graphql-schema'; +import { LicenseEntitlementType, PlatformRole } from '@/core/apollo/generated/graphql-schema'; import PageContentBlock from '@/core/ui/content/PageContentBlock'; import useNewVirtualContributorWizard from '../newVirtualContributorWizard/useNewVirtualContributorWizard'; import CampaignBlockCreateVC from './CampaignBlockCreateVC'; @@ -10,11 +10,12 @@ const CampaignBlock = () => { // Do not remove: Inside the blocks startWizard() is being called with a ClickEvent and that messes up with the param that startWizard expects const handleStartWizard = () => startWizard(); - const userRoles: CredentialType[] | undefined = data?.me.user?.agent.credentials?.map(credential => credential.type); + const userRoles: PlatformRole[] = data?.platform.myRoles ?? []; const userEntitlements: LicenseEntitlementType[] | undefined = data?.me.user?.account?.license?.myLicensePrivileges; - const rolesAvailableTo = [CredentialType.VcCampaign, CredentialType.BetaTester]; + const rolesAvailableTo = [PlatformRole.VcCampaign]; const entitlementsAvailableTo = [LicenseEntitlementType.AccountVirtualContributor]; + // the campaign block should be visible only for VcCampaign users ATM if ( !userRoles?.some(role => rolesAvailableTo.includes(role)) || !userEntitlements?.some(entitlement => entitlementsAvailableTo.includes(entitlement)) diff --git a/src/main/ui/platformNavigation/PlatformNavigationUserAvatar.tsx b/src/main/ui/platformNavigation/PlatformNavigationUserAvatar.tsx index ae817d2650..3736d1705a 100644 --- a/src/main/ui/platformNavigation/PlatformNavigationUserAvatar.tsx +++ b/src/main/ui/platformNavigation/PlatformNavigationUserAvatar.tsx @@ -9,6 +9,8 @@ import MenuTriggerButton from '@/core/ui/tooltip/MenuTriggerButton'; import { PLATFORM_NAVIGATION_MENU_Z_INDEX } from './constants'; import NavigationItemContainer from '@/core/ui/navigation/NavigationItemContainer'; import { useTranslation } from 'react-i18next'; +import BadgeLabel from '@/core/ui/icon/BadgeLabel'; +import { PlatformRole } from '@/core/apollo/generated/graphql-schema'; interface PlatformNavigationUserAvatarProps { children: ReactElement<{ onClose?: () => void }>; @@ -17,10 +19,12 @@ interface PlatformNavigationUserAvatarProps { const PlatformNavigationUserAvatar = ({ drawer, children }: PlatformNavigationUserAvatarProps) => { const { t } = useTranslation(); - const { user, isAuthenticated, loadingMe } = useUserContext(); + const { user, isAuthenticated, loadingMe, platformRoles } = useUserContext(); const theme = useTheme(); + const showBetaBadge = user && isAuthenticated && platformRoles.includes(PlatformRole.BetaTester); + return ( ( - } position="relative"> + } position="relative" overflow="visible"> } + {showBetaBadge && ( + + )} { - if (isAdmin) { - // TODO change role name path - return t('common.enums.authorization-credentials.GLOBAL_ADMIN.name'); + for (const platformRole of platformRoles) { + switch (platformRole) { + case PlatformRole.GlobalAdmin: + return t('common.roles.GLOBAL_ADMIN'); + case PlatformRole.Support: + return t('common.roles.SUPPORT'); + case PlatformRole.LicenseManager: + return t('common.roles.LICENSE_MANAGER'); + case PlatformRole.BetaTester: + return t('common.roles.BETA_TESTER'); + case PlatformRole.VcCampaign: + return t('common.roles.VC_CAMPAIGN'); + default: + return null; + } } - }, [isAdmin, t]); + }, [platformRoles, t]); const Wrapper = surface ? GlobalMenuSurface : Box; @@ -67,7 +81,12 @@ const PlatformNavigationUserMenu = forwardRef {user && ( - + {user.profile.displayName} {role && (