Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature(frontend): Contact Us Modal #2835

Merged
merged 3 commits into from
Jun 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions web/src/assets/plushie.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
73 changes: 73 additions & 0 deletions web/src/components/ContactUs/ContactUs.styled.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import styled from 'styled-components';
import {Modal as AntModal, Button, Typography} from 'antd';
import Plushie from 'assets/plushie.svg';

export const Container = styled.div`
position: absolute;
right: 12px;
bottom: 12px;
cursor: pointer;
`;

export const PlushieImage = styled.img.attrs({
src: Plushie,
})``;

export const PulseButtonContainer = styled.div`
position: absolute;
left: 2px;
top: -4px;
`;

export const ModalFooter = styled.div`
display: flex;
flex-direction: column;
gap: 12px;
`;

export const FullWidthButton = styled(Button)`
&& {
&& {
width: 100%;
margin: 0px;
}
}
`;

export const Header = styled.div`
display: flex;
flex-direction: column;
align-items: center;
gap: 12px;
`;

export const Title = styled(Typography.Title)``;

export const Message = styled(Typography.Paragraph)`
&& {
margin: 0;
}
`;

export const Modal = styled(AntModal).attrs({
width: 'auto',
})`
right: 32px;
top: calc(100vh - 410px);
position: absolute;
height: max-content;
padding: 0;

.ant-modal-content {
width: 340px;
}

.ant-modal-footer {
border: none;
padding-top: 0;
}

.ant-modal-body {
padding-bottom: 19px;
}
`;
35 changes: 35 additions & 0 deletions web/src/components/ContactUs/ContactUs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import {createContext, useContext, useMemo, useState} from 'react';
import {noop} from 'lodash';
import * as S from './ContactUs.styled';
import ContactUsModal from './ContactUsModal';
import PulseButton from '../PulseButton';

interface IContext {
onOpen(): void;
}

export const Context = createContext<IContext>({
onOpen: noop,
});

export const useContactUsModal = () => useContext(Context);

const ContactUs: React.FC = ({children}) => {
const [isOpen, setIsOpen] = useState(false);
const value = useMemo<IContext>(() => ({onOpen: () => setIsOpen(true)}), []);

return (
<Context.Provider value={value}>
{children}
<S.Container onClick={() => setIsOpen(true)}>
<S.PulseButtonContainer>
<PulseButton />
</S.PulseButtonContainer>
<S.PlushieImage />
</S.Container>
<ContactUsModal isOpen={isOpen} onClose={() => setIsOpen(false)} />
</Context.Provider>
);
};

export default ContactUs;
24 changes: 24 additions & 0 deletions web/src/components/ContactUs/ContactUsModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import * as S from './ContactUs.styled';
import ContactUsModalFooter from './ContactUsModalFooter';

interface IProps {
isOpen: boolean;
onClose(): void;
}

const ContactUsModal = ({isOpen, onClose}: IProps) => {
return (
<S.Modal visible={isOpen} onCancel={onClose} footer={<ContactUsModalFooter />}>
<S.Header>
<S.PlushieImage width="100px" height="auto" />
<S.Title>Let us help you</S.Title>
</S.Header>
<S.Message>
Technical glitches can be tricky, even for the best of us. Don&apos;t fret! We&apos;re here to save your day.
Create an Issue or contact us via Discord and our tech-savvy team are ready to lend a helping hand.
</S.Message>
</S.Modal>
);
};

export default ContactUsModal;
19 changes: 19 additions & 0 deletions web/src/components/ContactUs/ContactUsModalFooter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import {DISCORD_URL, GITHUB_ISSUES_URL} from 'constants/Common.constants';
import * as S from './ContactUs.styled';

const ContactUsModalFooter = () => {
return (
<S.ModalFooter>
<a href={GITHUB_ISSUES_URL} target="_blank">
<S.FullWidthButton type="primary">Create an Issue</S.FullWidthButton>
</a>
<a href={DISCORD_URL} target="_blank">
<S.FullWidthButton ghost type="primary">
Contact Team on Discord
</S.FullWidthButton>
</a>
</S.ModalFooter>
);
};

export default ContactUsModalFooter;
5 changes: 5 additions & 0 deletions web/src/components/ContactUs/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import {useContactUsModal} from './ContactUs';

// eslint-disable-next-line no-restricted-exports
export {default} from './ContactUs';
export {useContactUsModal};
31 changes: 31 additions & 0 deletions web/src/components/PulseButton/PulseButton.styled.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import styled, {DefaultTheme, keyframes} from 'styled-components';

const getPulseAnimation = (theme: DefaultTheme) =>
keyframes`
0% {
transform: scale(.7);
box-shadow: 0 0 0 0 ${theme.color.primaryLight};
}
70% {
transform: scale(1);
box-shadow: 0 0 0 2px ${theme.color.primaryLight};
}
100% {
transform: scale(.7);
box-shadow: 0 0 0 0 ${theme.color.primaryLight};
}
`;

export const PulseButton = styled.button`
width: 9px;
height: 9px;
border: none;
padding: 0px;
border-radius: 50%;
cursor: pointer;
background: ${({theme}) => theme.color.primary};

animation-name: ${({theme}) => getPulseAnimation(theme)};
animation-duration: 1.5s;
animation-iteration-count: infinite;
`;
3 changes: 3 additions & 0 deletions web/src/components/PulseButton/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import {PulseButton} from './PulseButton.styled';

export default PulseButton;
13 changes: 8 additions & 5 deletions web/src/pages/Settings/Settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,17 @@ import withAnalytics from 'components/WithAnalytics/WithAnalytics';
import DataStoreProvider from 'providers/DataStore';
import SettingsProvider from 'providers/Settings';
import Content from './Content';
import ContactUs from '../../components/ContactUs/ContactUs';

const Settings = () => (
<Layout hasMenu>
<DataStoreProvider>
<SettingsProvider>
<Content />
</SettingsProvider>
</DataStoreProvider>
<ContactUs>
<DataStoreProvider>
<SettingsProvider>
<Content />
</SettingsProvider>
</DataStoreProvider>
</ContactUs>
</Layout>
);

Expand Down
10 changes: 9 additions & 1 deletion web/src/providers/DataStore/DataStore.provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
useDeleteDataStoreMutation,
} from 'redux/apis/TraceTest.api';
import DataStoreService from 'services/DataStore.service';
import {useContactUsModal} from 'components/ContactUs';
import {SupportedDataStores, TConnectionResult, TDraftDataStore} from 'types/DataStore.types';
import DataStore from 'models/DataStore.model';
import useDataStoreNotification from './hooks/useDataStoreNotification';
Expand Down Expand Up @@ -49,6 +50,8 @@ const DataStoreProvider = ({children}: IProps) => {
const [isFormValid, setIsFormValid] = useState(false);
const {showSuccessNotification, showTestConnectionNotification} = useDataStoreNotification();
const {onOpen} = useConfirmationModal();
const [connectionTries, setConnectionTries] = useState(0);
const {onOpen: onContactUsOpen} = useContactUsModal();

const onSaveConfig = useCallback(
async (draft: TDraftDataStore, defaultDataStore: DataStore) => {
Expand Down Expand Up @@ -105,11 +108,16 @@ const DataStoreProvider = ({children}: IProps) => {
try {
const result = await testConnection(dataStore.spec!).unwrap();
showTestConnectionNotification(result, draft.dataStoreType!);
setConnectionTries(0);
} catch (err) {
setConnectionTries(prev => prev + 1);
showTestConnectionNotification(err as TConnectionResult, draft.dataStoreType!);
if (connectionTries + 1 === 3) {
onContactUsOpen();
}
}
},
[showTestConnectionNotification, testConnection]
[connectionTries, onContactUsOpen, showTestConnectionNotification, testConnection]
);

const value = useMemo<IContext>(
Expand Down