fix #6127 updated support button (#6422)

fix #6127 

updated the Support button toopen a menu that allows users to either
access the [user guide](https://twenty.com/user-guide) or contact
support through the chat.

![Screenshot
(675)](https://github.com/user-attachments/assets/026e48ee-c397-44ae-bee7-e76698298a52)

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
NitinPSingh 2024-07-27 22:10:34 +05:30 committed by GitHub
parent 6728e40256
commit 9d51af3b41
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 72 additions and 15 deletions

View File

@ -3,7 +3,7 @@ import { useRecoilValue, useSetRecoilState } from 'recoil';
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState'; import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { SettingsNavigationDrawerItems } from '@/settings/components/SettingsNavigationDrawerItems'; import { SettingsNavigationDrawerItems } from '@/settings/components/SettingsNavigationDrawerItems';
import { SupportChat } from '@/support/components/SupportChat'; import { SupportDropdown } from '@/support/components/SupportDropdown';
import { GithubVersionLink } from '@/ui/navigation/link/components/GithubVersionLink'; import { GithubVersionLink } from '@/ui/navigation/link/components/GithubVersionLink';
import { import {
NavigationDrawer, NavigationDrawer,
@ -53,7 +53,7 @@ export const AppNavigationDrawer = ({
undefined, undefined,
title: currentWorkspace?.displayName ?? undefined, title: currentWorkspace?.displayName ?? undefined,
children: <MainNavigationDrawerItems />, children: <MainNavigationDrawerItems />,
footer: <SupportChat />, footer: <SupportDropdown />,
}; };
useEffect(() => { useEffect(() => {

View File

@ -1,6 +1,6 @@
import { useCallback, useEffect, useState } from 'react';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { isNonEmptyString } from '@sniptt/guards'; import { isNonEmptyString } from '@sniptt/guards';
import { useCallback, useEffect, useState } from 'react';
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import { IconHelpCircle } from 'twenty-ui'; import { IconHelpCircle } from 'twenty-ui';
@ -8,7 +8,7 @@ import { currentUserState } from '@/auth/states/currentUserState';
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState'; import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
import { supportChatState } from '@/client-config/states/supportChatState'; import { supportChatState } from '@/client-config/states/supportChatState';
import { useIsPrefetchLoading } from '@/prefetch/hooks/useIsPrefetchLoading'; import { useIsPrefetchLoading } from '@/prefetch/hooks/useIsPrefetchLoading';
import { SupportChatSkeletonLoader } from '@/support/components/SupportChatSkeletonLoader'; import { SupportButtonSkeletonLoader } from '@/support/components/SupportButtonSkeletonLoader';
import { Button } from '@/ui/input/button/components/Button'; import { Button } from '@/ui/input/button/components/Button';
import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember'; import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember';
import { User } from '~/generated/graphql'; import { User } from '~/generated/graphql';
@ -34,7 +34,7 @@ const insertScript = ({
document.body.appendChild(script); document.body.appendChild(script);
}; };
export const SupportChat = () => { export const SupportButton = () => {
const currentUser = useRecoilValue(currentUserState); const currentUser = useRecoilValue(currentUserState);
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState); const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
const supportChat = useRecoilValue(supportChatState); const supportChat = useRecoilValue(supportChatState);
@ -102,7 +102,7 @@ export const SupportChat = () => {
]); ]);
if (loading) { if (loading) {
return <SupportChatSkeletonLoader />; return <SupportButtonSkeletonLoader />;
} }
return isFrontChatLoaded ? ( return isFrontChatLoaded ? (

View File

@ -1,7 +1,7 @@
import Skeleton, { SkeletonTheme } from 'react-loading-skeleton'; import Skeleton, { SkeletonTheme } from 'react-loading-skeleton';
import { useTheme } from '@emotion/react'; import { useTheme } from '@emotion/react';
export const SupportChatSkeletonLoader = () => { export const SupportButtonSkeletonLoader = () => {
const theme = useTheme(); const theme = useTheme();
return ( return (
<SkeletonTheme <SkeletonTheme

View File

@ -0,0 +1,51 @@
import { SupportButton } from '@/support/components/SupportButton';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { IconHelpCircle, IconMessage } from 'twenty-ui';
export const SupportDropdown = () => {
const dropdownId = `support-field-active-action-dropdown`;
const { closeDropdown } = useDropdown(dropdownId);
const handleTalkToUs = () => {
window.FrontChat?.('show');
closeDropdown();
};
const handleUserGuide = () => {
window.open('https://twenty.com/user-guide', '_blank');
closeDropdown();
};
return (
<Dropdown
dropdownId={dropdownId}
dropdownPlacement="top-start"
dropdownOffset={{ x: 0, y: -28 }}
clickableComponent={<SupportButton />}
dropdownComponents={
<DropdownMenu width="160px">
<DropdownMenuItemsContainer>
<MenuItem
text="Talk to us"
LeftIcon={IconMessage}
onClick={handleTalkToUs}
/>
<MenuItem
text="Documentation"
LeftIcon={IconHelpCircle}
onClick={handleUserGuide}
/>
</DropdownMenuItemsContainer>
</DropdownMenu>
}
dropdownHotkeyScope={{
scope: dropdownId,
}}
/>
);
};

View File

@ -1,6 +1,6 @@
import { expect } from '@storybook/jest'; import { expect } from '@storybook/jest';
import { Meta, StoryObj } from '@storybook/react'; import { Meta, StoryObj } from '@storybook/react';
import { within } from '@storybook/test'; import { within, userEvent } from '@storybook/test';
import { useSetRecoilState } from 'recoil'; import { useSetRecoilState } from 'recoil';
import { currentUserState } from '@/auth/states/currentUserState'; import { currentUserState } from '@/auth/states/currentUserState';
@ -14,11 +14,11 @@ import {
mockedWorkspaceMemberData, mockedWorkspaceMemberData,
} from '~/testing/mock-data/users'; } from '~/testing/mock-data/users';
import { SupportChat } from '../SupportChat'; import { SupportDropdown } from '@/support/components/SupportDropdown';
const meta: Meta<typeof SupportChat> = { const meta: Meta<typeof SupportDropdown> = {
title: 'Modules/Support/SupportChat', title: 'Modules/Support/SupportDropdown',
component: SupportChat, component: SupportDropdown,
decorators: [ decorators: [
(Story) => { (Story) => {
const setCurrentUser = useSetRecoilState(currentUserState); const setCurrentUser = useSetRecoilState(currentUserState);
@ -42,11 +42,16 @@ const meta: Meta<typeof SupportChat> = {
}; };
export default meta; export default meta;
type Story = StoryObj<typeof SupportChat>; type Story = StoryObj<typeof SupportDropdown>;
export const Default: Story = { export const Default: Story = {
play: async () => { play: async () => {
const canvas = within(document.body); const canvas = within(document.body);
expect(await canvas.findByText('Support')).toBeInTheDocument(); expect(await canvas.findByText('Support')).toBeInTheDocument();
await userEvent.click(canvas.getByText('Support'));
expect(await canvas.findByText('Documentation')).toBeInTheDocument();
expect(await canvas.findByText('Talk to us')).toBeInTheDocument();
}, },
}; };

View File

@ -14,7 +14,7 @@ import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { SettingsBillingCoverImage } from '@/billing/components/SettingsBillingCoverImage'; import { SettingsBillingCoverImage } from '@/billing/components/SettingsBillingCoverImage';
import { useOnboardingStatus } from '@/onboarding/hooks/useOnboardingStatus'; import { useOnboardingStatus } from '@/onboarding/hooks/useOnboardingStatus';
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer'; import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
import { SupportChat } from '@/support/components/SupportChat'; import { SupportButton } from '@/support/components/SupportButton';
import { AppPath } from '@/types/AppPath'; import { AppPath } from '@/types/AppPath';
import { Info } from '@/ui/display/info/components/Info'; import { Info } from '@/ui/display/info/components/Info';
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
@ -218,7 +218,7 @@ export const SettingsBilling = () => {
)} )}
</SettingsPageContainer> </SettingsPageContainer>
<StyledInvisibleChat> <StyledInvisibleChat>
<SupportChat /> <SupportButton />
</StyledInvisibleChat> </StyledInvisibleChat>
<ConfirmationModal <ConfirmationModal
isOpen={isSwitchingIntervalModalOpen} isOpen={isSwitchingIntervalModalOpen}

View File

@ -116,6 +116,7 @@ export {
IconMailCog, IconMailCog,
IconMap, IconMap,
IconMaximize, IconMaximize,
IconMessage,
IconMinus, IconMinus,
IconMoneybag, IconMoneybag,
IconMouse2, IconMouse2,