diff --git a/web/packages/teleport/src/Users/Users.story.tsx b/web/packages/teleport/src/Users/Users.story.tsx index 38d07d5193197..fc905715582c2 100644 --- a/web/packages/teleport/src/Users/Users.story.tsx +++ b/web/packages/teleport/src/Users/Users.story.tsx @@ -47,6 +47,14 @@ export const Loaded = () => { ); }; +export const UsersNotEqualMauNotice = () => { + return ( + + + + ); +}; + export const Failed = () => { const attempt = { isProcessing: false, @@ -139,4 +147,6 @@ const sample = { InviteCollaborators: null, onEmailPasswordResetClose: () => null, EmailPasswordReset: null, + showMauInfo: false, + onDismissUsersMauNotice: () => null, }; diff --git a/web/packages/teleport/src/Users/Users.test.tsx b/web/packages/teleport/src/Users/Users.test.tsx index a05f70bf8550f..154047002d29e 100644 --- a/web/packages/teleport/src/Users/Users.test.tsx +++ b/web/packages/teleport/src/Users/Users.test.tsx @@ -18,7 +18,7 @@ import React from 'react'; import { MemoryRouter } from 'react-router'; -import { render, screen } from 'design/utils/testing'; +import { render, screen, userEvent } from 'design/utils/testing'; import { ContextProvider } from 'teleport'; import { createTeleportContext } from 'teleport/mocks/contexts'; @@ -57,6 +57,8 @@ describe('invite collaborators integration', () => { inviteCollaboratorsOpen: false, onEmailPasswordResetClose: () => undefined, EmailPasswordReset: null, + showMauInfo: false, + onDismissUsersMauNotice: () => null, }; }); @@ -105,6 +107,55 @@ describe('invite collaborators integration', () => { }); }); +test('Users not equal to MAU Notice', async () => { + const ctx = createTeleportContext(); + let props: State; + + props = { + attempt: { + message: 'success', + isSuccess: true, + isProcessing: false, + isFailed: false, + }, + users: [], + fetchRoles: async () => [], + operation: { type: 'invite-collaborators' }, + onStartCreate: () => undefined, + onStartDelete: () => undefined, + onStartEdit: () => undefined, + onStartReset: () => undefined, + onStartInviteCollaborators: () => undefined, + onClose: () => undefined, + onDelete: () => undefined, + onCreate: () => undefined, + onUpdate: () => undefined, + onReset: () => undefined, + onInviteCollaboratorsClose: () => undefined, + InviteCollaborators: null, + inviteCollaboratorsOpen: false, + onEmailPasswordResetClose: () => undefined, + EmailPasswordReset: null, + showMauInfo: true, + onDismissUsersMauNotice: jest.fn(), + }; + + const user = userEvent.setup(); + + render( + + + + + + ); + + expect(screen.getByTestId('users-not-mau-alert')).toBeInTheDocument(); + await user.click(screen.getByRole('button', { name: 'Dismiss' })); + expect(props.onDismissUsersMauNotice).toHaveBeenCalled(); + expect(screen.queryByTestId('users-not-mau-alert')).not.toBeInTheDocument(); +}); + describe('email password reset integration', () => { const ctx = createTeleportContext(); @@ -139,6 +190,8 @@ describe('email password reset integration', () => { inviteCollaboratorsOpen: false, onEmailPasswordResetClose: () => undefined, EmailPasswordReset: null, + showMauInfo: false, + onDismissUsersMauNotice: () => null, }; }); diff --git a/web/packages/teleport/src/Users/Users.tsx b/web/packages/teleport/src/Users/Users.tsx index 220576e48d5ec..f36b8d70bf6d0 100644 --- a/web/packages/teleport/src/Users/Users.tsx +++ b/web/packages/teleport/src/Users/Users.tsx @@ -17,7 +17,7 @@ */ import React from 'react'; -import { Indicator, Box, Alert, Button } from 'design'; +import { Indicator, Box, Alert, Button, Link } from 'design'; import { FeatureBox, @@ -46,6 +46,8 @@ export function Users(props: State) { onStartDelete, onStartEdit, onStartReset, + showMauInfo, + onDismissUsersMauNotice, onClose, onCreate, onUpdate, @@ -98,6 +100,40 @@ export function Users(props: State) { )} + {showMauInfo && ( + theme.colors.buttons.link.default}; + } + `} + > + The users displayed here are not an accurate reflection of Monthly + Active Users (MAU). For example, users who log in through Single + Sign-On (SSO) providers such as Okta may only appear here temporarily + and disappear once their sessions expire. For more information, read + our documentation on{' '} + + MAU + {' '} + and{' '} + + User Types + + . + + )} {attempt.isFailed && } {attempt.isSuccess && ( r.name); } + function onDismissUsersMauNotice() { + storageService.setUsersMAUAcknowledged(); + } + useEffect(() => { attemptActions.do(() => ctx.userService.fetchUsers().then(setUsers)); }, []); + // if the cluster has billing enabled, and usageBasedBilling, and they haven't acknowledged + // the info yet + const showMauInfo = + ctx.getFeatureFlags().billing && + cfg.isUsageBasedBilling && + !storageService.getUsersMauAcknowledged(); + return { attempt, users, @@ -143,6 +156,8 @@ export default function useUsers({ inviteCollaboratorsOpen, onEmailPasswordResetClose, EmailPasswordReset, + showMauInfo, + onDismissUsersMauNotice, }; } diff --git a/web/packages/teleport/src/services/storageService/storageService.ts b/web/packages/teleport/src/services/storageService/storageService.ts index af9a3601d9c46..644543046f59d 100644 --- a/web/packages/teleport/src/services/storageService/storageService.ts +++ b/web/packages/teleport/src/services/storageService/storageService.ts @@ -43,6 +43,7 @@ const KEEP_LOCALSTORAGE_KEYS_ON_LOGOUT = [ KeysEnum.USER_PREFERENCES, KeysEnum.RECOMMEND_FEATURE, KeysEnum.LICENSE_ACKNOWLEDGED, + KeysEnum.USERS_NOT_EQUAL_TO_MAU_ACKNOWLEDGED, ]; export const storageService = { @@ -207,6 +208,21 @@ export const storageService = { window.localStorage.setItem(KeysEnum.LICENSE_ACKNOWLEDGED, 'true'); }, + getUsersMauAcknowledged(): boolean { + return ( + window.localStorage.getItem( + KeysEnum.USERS_NOT_EQUAL_TO_MAU_ACKNOWLEDGED + ) === 'true' + ); + }, + + setUsersMAUAcknowledged() { + window.localStorage.setItem( + KeysEnum.USERS_NOT_EQUAL_TO_MAU_ACKNOWLEDGED, + 'true' + ); + }, + broadcast(messageType, messageBody) { window.localStorage.setItem(messageType, messageBody); window.localStorage.removeItem(messageType); diff --git a/web/packages/teleport/src/services/storageService/types.ts b/web/packages/teleport/src/services/storageService/types.ts index d604d289bfa7e..71ad6d5992ab7 100644 --- a/web/packages/teleport/src/services/storageService/types.ts +++ b/web/packages/teleport/src/services/storageService/types.ts @@ -33,7 +33,8 @@ export const KeysEnum = { EXTERNAL_AUDIT_STORAGE_CTA_DISABLED: 'grv_teleport_external_audit_storage_disabled', LICENSE_ACKNOWLEDGED: 'grv_teleport_license_acknowledged', - + USERS_NOT_EQUAL_TO_MAU_ACKNOWLEDGED: + 'grv_users_not_equal_to_mau_acknowledged', LOCAL_NOTIFICATION_STATES: 'grv_teleport_notification_states', };