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..77ff4c39a91b9 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,68 @@ describe('invite collaborators integration', () => { }); }); +test('Users not equal to MAU Notice', async () => { + const user = userEvent.setup(); + const ctx = createTeleportContext(); + + const props: State = { + attempt: { + message: 'success', + isSuccess: true, + isProcessing: false, + isFailed: false, + }, + users: [], + fetchRoles: () => Promise.resolve([]), + operation: { + type: 'reset', + user: { name: 'alice@example.com', roles: ['foo'] }, + }, + + 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 { rerender } = render( + + + + + + ); + + expect(screen.getByTestId('users-not-mau-alert')).toBeInTheDocument(); + await user.click(screen.getByTestId('dismiss-users-not-mau-alert')); + expect(props.onDismissUsersMauNotice).toHaveBeenCalled(); + + const newProps = { ...props, showMauInfo: false }; + + rerender( + + + + + + ); + + expect(screen.queryByTestId('users-not-mau-alert')).not.toBeInTheDocument(); +}); + describe('email password reset integration', () => { const ctx = createTeleportContext(); @@ -139,6 +203,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 38907d59c425e..c03a61418824e 100644 --- a/web/packages/teleport/src/Users/Users.tsx +++ b/web/packages/teleport/src/Users/Users.tsx @@ -17,7 +17,8 @@ */ import React from 'react'; -import { Indicator, Box, ButtonPrimary, Alert } from 'design'; +import { Indicator, Box, Alert, ButtonPrimary, Link, ButtonIcon } from 'design'; +import { Cross } from 'design/Icon'; import { FeatureBox, @@ -46,6 +47,8 @@ export function Users(props: State) { onStartDelete, onStartEdit, onStartReset, + showMauInfo, + onDismissUsersMauNotice, onClose, onCreate, onUpdate, @@ -86,6 +89,34 @@ export function Users(props: State) { )} + {showMauInfo && ( + + + 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(); + setShowMauInfo(false); + } + useEffect(() => { attemptActions.do(() => ctx.userService.fetchUsers().then(setUsers)); }, []); @@ -143,6 +157,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', };