mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-12 22:39:10 +08:00
feat: move chat support behind paywall (#5673)
* feat: move chat support behind paywall * feat: wire up chat support paywall * feat: move chat support code from app layout to separate component * feat: add log events
This commit is contained in:
parent
6c634b99d0
commit
1308f0f15f
@ -66,6 +66,14 @@ function App(): JSX.Element {
|
|||||||
allFlags.find((flag) => flag.name === FeatureKeys.CHAT_SUPPORT)?.active ||
|
allFlags.find((flag) => flag.name === FeatureKeys.CHAT_SUPPORT)?.active ||
|
||||||
false;
|
false;
|
||||||
|
|
||||||
|
const isPremiumSupportEnabled =
|
||||||
|
allFlags.find((flag) => flag.name === FeatureKeys.PREMIUM_SUPPORT)?.active ||
|
||||||
|
false;
|
||||||
|
|
||||||
|
const showAddCreditCardModal =
|
||||||
|
!isPremiumSupportEnabled &&
|
||||||
|
!licenseData?.payload?.trialConvertedToSubscription;
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: UPDATE_FEATURE_FLAG_RESPONSE,
|
type: UPDATE_FEATURE_FLAG_RESPONSE,
|
||||||
payload: {
|
payload: {
|
||||||
@ -82,7 +90,7 @@ function App(): JSX.Element {
|
|||||||
setRoutes(newRoutes);
|
setRoutes(newRoutes);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isLoggedInState && isChatSupportEnabled) {
|
if (isLoggedInState && isChatSupportEnabled && !showAddCreditCardModal) {
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
window.Intercom('boot', {
|
window.Intercom('boot', {
|
||||||
|
@ -0,0 +1,136 @@
|
|||||||
|
import { Button, Modal, Typography } from 'antd';
|
||||||
|
import updateCreditCardApi from 'api/billing/checkout';
|
||||||
|
import logEvent from 'api/common/logEvent';
|
||||||
|
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
||||||
|
import useLicense from 'hooks/useLicense';
|
||||||
|
import { useNotifications } from 'hooks/useNotifications';
|
||||||
|
import { CreditCard, X } from 'lucide-react';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { useMutation } from 'react-query';
|
||||||
|
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||||
|
import { CheckoutSuccessPayloadProps } from 'types/api/billing/checkout';
|
||||||
|
import { License } from 'types/api/licenses/def';
|
||||||
|
|
||||||
|
export default function ChatSupportGateway(): JSX.Element {
|
||||||
|
const { notifications } = useNotifications();
|
||||||
|
const [activeLicense, setActiveLicense] = useState<License | null>(null);
|
||||||
|
|
||||||
|
const [isAddCreditCardModalOpen, setIsAddCreditCardModalOpen] = useState(
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
const { data: licenseData, isFetching } = useLicense();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const activeValidLicense =
|
||||||
|
licenseData?.payload?.licenses?.find(
|
||||||
|
(license) => license.isCurrent === true,
|
||||||
|
) || null;
|
||||||
|
|
||||||
|
setActiveLicense(activeValidLicense);
|
||||||
|
}, [licenseData, isFetching]);
|
||||||
|
|
||||||
|
const handleBillingOnSuccess = (
|
||||||
|
data: ErrorResponse | SuccessResponse<CheckoutSuccessPayloadProps, unknown>,
|
||||||
|
): void => {
|
||||||
|
if (data?.payload?.redirectURL) {
|
||||||
|
const newTab = document.createElement('a');
|
||||||
|
newTab.href = data.payload.redirectURL;
|
||||||
|
newTab.target = '_blank';
|
||||||
|
newTab.rel = 'noopener noreferrer';
|
||||||
|
newTab.click();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleBillingOnError = (): void => {
|
||||||
|
notifications.error({
|
||||||
|
message: SOMETHING_WENT_WRONG,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const { mutate: updateCreditCard, isLoading: isLoadingBilling } = useMutation(
|
||||||
|
updateCreditCardApi,
|
||||||
|
{
|
||||||
|
onSuccess: (data) => {
|
||||||
|
handleBillingOnSuccess(data);
|
||||||
|
},
|
||||||
|
onError: handleBillingOnError,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleAddCreditCard = (): void => {
|
||||||
|
logEvent('Add Credit card modal: Clicked', {
|
||||||
|
source: `intercom icon`,
|
||||||
|
page: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
updateCreditCard({
|
||||||
|
licenseKey: activeLicense?.key || '',
|
||||||
|
successURL: window.location.href,
|
||||||
|
cancelURL: window.location.href,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="chat-support-gateway">
|
||||||
|
<Button
|
||||||
|
className="chat-support-gateway-btn"
|
||||||
|
onClick={(): void => {
|
||||||
|
logEvent('Disabled Chat Support: Clicked', {
|
||||||
|
source: `intercom icon`,
|
||||||
|
page: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
setIsAddCreditCardModalOpen(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 28 32"
|
||||||
|
className="chat-support-gateway-btn-icon"
|
||||||
|
>
|
||||||
|
<path d="M28 32s-4.714-1.855-8.527-3.34H3.437C1.54 28.66 0 27.026 0 25.013V3.644C0 1.633 1.54 0 3.437 0h21.125c1.898 0 3.437 1.632 3.437 3.645v18.404H28V32zm-4.139-11.982a.88.88 0 00-1.292-.105c-.03.026-3.015 2.681-8.57 2.681-5.486 0-8.517-2.636-8.571-2.684a.88.88 0 00-1.29.107 1.01 1.01 0 00-.219.708.992.992 0 00.318.664c.142.128 3.537 3.15 9.762 3.15 6.226 0 9.621-3.022 9.763-3.15a.992.992 0 00.317-.664 1.01 1.01 0 00-.218-.707z" />
|
||||||
|
</svg>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Add Credit Card Modal */}
|
||||||
|
<Modal
|
||||||
|
className="add-credit-card-modal"
|
||||||
|
title={<span className="title">Add Credit Card for Chat Support</span>}
|
||||||
|
open={isAddCreditCardModalOpen}
|
||||||
|
closable
|
||||||
|
onCancel={(): void => setIsAddCreditCardModalOpen(false)}
|
||||||
|
destroyOnClose
|
||||||
|
footer={[
|
||||||
|
<Button
|
||||||
|
key="cancel"
|
||||||
|
onClick={(): void => setIsAddCreditCardModalOpen(false)}
|
||||||
|
className="cancel-btn"
|
||||||
|
icon={<X size={16} />}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>,
|
||||||
|
<Button
|
||||||
|
key="submit"
|
||||||
|
type="primary"
|
||||||
|
icon={<CreditCard size={16} />}
|
||||||
|
size="middle"
|
||||||
|
loading={isLoadingBilling}
|
||||||
|
disabled={isLoadingBilling}
|
||||||
|
onClick={handleAddCreditCard}
|
||||||
|
className="add-credit-card-btn"
|
||||||
|
>
|
||||||
|
Add Credit Card
|
||||||
|
</Button>,
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Typography.Text className="add-credit-card-text">
|
||||||
|
You're currently on <span className="highlight-text">Trial plan</span>
|
||||||
|
. Add a credit card to access SigNoz chat support to your workspace.
|
||||||
|
</Typography.Text>
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
184
frontend/src/components/LaunchChatSupport/LaunchChatSupport.tsx
Normal file
184
frontend/src/components/LaunchChatSupport/LaunchChatSupport.tsx
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
import './LaunchChatSupport.styles.scss';
|
||||||
|
|
||||||
|
import { Button, Modal, Tooltip, Typography } from 'antd';
|
||||||
|
import updateCreditCardApi from 'api/billing/checkout';
|
||||||
|
import logEvent from 'api/common/logEvent';
|
||||||
|
import cx from 'classnames';
|
||||||
|
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
||||||
|
import { FeatureKeys } from 'constants/features';
|
||||||
|
import useFeatureFlags from 'hooks/useFeatureFlag';
|
||||||
|
import useLicense from 'hooks/useLicense';
|
||||||
|
import { useNotifications } from 'hooks/useNotifications';
|
||||||
|
import { defaultTo } from 'lodash-es';
|
||||||
|
import { CreditCard, HelpCircle, X } from 'lucide-react';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { useMutation } from 'react-query';
|
||||||
|
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||||
|
import { CheckoutSuccessPayloadProps } from 'types/api/billing/checkout';
|
||||||
|
import { License } from 'types/api/licenses/def';
|
||||||
|
import { isCloudUser } from 'utils/app';
|
||||||
|
|
||||||
|
export interface LaunchChatSupportProps {
|
||||||
|
eventName: string;
|
||||||
|
attributes: Record<string, unknown>;
|
||||||
|
message?: string;
|
||||||
|
buttonText?: string;
|
||||||
|
className?: string;
|
||||||
|
onHoverText?: string;
|
||||||
|
intercomMessageDisabled?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||||
|
function LaunchChatSupport({
|
||||||
|
attributes,
|
||||||
|
eventName,
|
||||||
|
message = '',
|
||||||
|
buttonText = '',
|
||||||
|
className = '',
|
||||||
|
onHoverText = '',
|
||||||
|
intercomMessageDisabled = false,
|
||||||
|
}: LaunchChatSupportProps): JSX.Element | null {
|
||||||
|
const isChatSupportEnabled = useFeatureFlags(FeatureKeys.CHAT_SUPPORT)?.active;
|
||||||
|
const isCloudUserVal = isCloudUser();
|
||||||
|
const { notifications } = useNotifications();
|
||||||
|
const { data: licenseData, isFetching } = useLicense();
|
||||||
|
const [activeLicense, setActiveLicense] = useState<License | null>(null);
|
||||||
|
const [isAddCreditCardModalOpen, setIsAddCreditCardModalOpen] = useState(
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
const isPremiumChatSupportEnabled =
|
||||||
|
useFeatureFlags(FeatureKeys.PREMIUM_SUPPORT)?.active || false;
|
||||||
|
|
||||||
|
const showAddCreditCardModal =
|
||||||
|
!isPremiumChatSupportEnabled &&
|
||||||
|
!licenseData?.payload?.trialConvertedToSubscription;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const activeValidLicense =
|
||||||
|
licenseData?.payload?.licenses?.find(
|
||||||
|
(license) => license.isCurrent === true,
|
||||||
|
) || null;
|
||||||
|
|
||||||
|
setActiveLicense(activeValidLicense);
|
||||||
|
}, [licenseData, isFetching]);
|
||||||
|
|
||||||
|
const handleFacingIssuesClick = (): void => {
|
||||||
|
if (showAddCreditCardModal) {
|
||||||
|
setIsAddCreditCardModalOpen(true);
|
||||||
|
} else {
|
||||||
|
logEvent(eventName, attributes);
|
||||||
|
if (window.Intercom && !intercomMessageDisabled) {
|
||||||
|
window.Intercom('showNewMessage', defaultTo(message, ''));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleBillingOnSuccess = (
|
||||||
|
data: ErrorResponse | SuccessResponse<CheckoutSuccessPayloadProps, unknown>,
|
||||||
|
): void => {
|
||||||
|
if (data?.payload?.redirectURL) {
|
||||||
|
const newTab = document.createElement('a');
|
||||||
|
newTab.href = data.payload.redirectURL;
|
||||||
|
newTab.target = '_blank';
|
||||||
|
newTab.rel = 'noopener noreferrer';
|
||||||
|
newTab.click();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleBillingOnError = (): void => {
|
||||||
|
notifications.error({
|
||||||
|
message: SOMETHING_WENT_WRONG,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const { mutate: updateCreditCard, isLoading: isLoadingBilling } = useMutation(
|
||||||
|
updateCreditCardApi,
|
||||||
|
{
|
||||||
|
onSuccess: (data) => {
|
||||||
|
handleBillingOnSuccess(data);
|
||||||
|
},
|
||||||
|
onError: handleBillingOnError,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleAddCreditCard = (): void => {
|
||||||
|
logEvent('Add Credit card modal: Clicked', {
|
||||||
|
source: `facing issues button`,
|
||||||
|
page: '',
|
||||||
|
...attributes,
|
||||||
|
});
|
||||||
|
|
||||||
|
updateCreditCard({
|
||||||
|
licenseKey: activeLicense?.key || '',
|
||||||
|
successURL: window.location.href,
|
||||||
|
cancelURL: window.location.href,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return isCloudUserVal && isChatSupportEnabled ? ( // Note: we would need to move this condition to license based in future
|
||||||
|
<div className="facing-issue-button">
|
||||||
|
<Tooltip
|
||||||
|
title={onHoverText}
|
||||||
|
autoAdjustOverflow
|
||||||
|
style={{ padding: 8 }}
|
||||||
|
overlayClassName="tooltip-overlay"
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
className={cx('periscope-btn', 'facing-issue-button', className)}
|
||||||
|
onClick={handleFacingIssuesClick}
|
||||||
|
icon={<HelpCircle size={14} />}
|
||||||
|
>
|
||||||
|
{buttonText || 'Facing issues?'}
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
|
{/* Add Credit Card Modal */}
|
||||||
|
<Modal
|
||||||
|
className="add-credit-card-modal"
|
||||||
|
title={<span className="title">Add Credit Card for Chat Support</span>}
|
||||||
|
open={isAddCreditCardModalOpen}
|
||||||
|
closable
|
||||||
|
onCancel={(): void => setIsAddCreditCardModalOpen(false)}
|
||||||
|
destroyOnClose
|
||||||
|
footer={[
|
||||||
|
<Button
|
||||||
|
key="cancel"
|
||||||
|
onClick={(): void => setIsAddCreditCardModalOpen(false)}
|
||||||
|
className="cancel-btn"
|
||||||
|
icon={<X size={16} />}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>,
|
||||||
|
<Button
|
||||||
|
key="submit"
|
||||||
|
type="primary"
|
||||||
|
icon={<CreditCard size={16} />}
|
||||||
|
size="middle"
|
||||||
|
loading={isLoadingBilling}
|
||||||
|
disabled={isLoadingBilling}
|
||||||
|
onClick={handleAddCreditCard}
|
||||||
|
className="add-credit-card-btn"
|
||||||
|
>
|
||||||
|
Add Credit Card
|
||||||
|
</Button>,
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Typography.Text className="add-credit-card-text">
|
||||||
|
You're currently on <span className="highlight-text">Trial plan</span>
|
||||||
|
. Add a credit card to access SigNoz chat support to your workspace.
|
||||||
|
</Typography.Text>
|
||||||
|
</Modal>
|
||||||
|
</div>
|
||||||
|
) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
LaunchChatSupport.defaultProps = {
|
||||||
|
message: '',
|
||||||
|
buttonText: '',
|
||||||
|
className: '',
|
||||||
|
onHoverText: '',
|
||||||
|
intercomMessageDisabled: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default LaunchChatSupport;
|
@ -5,7 +5,7 @@ import { Button, Dropdown, Flex, MenuProps, Switch } from 'antd';
|
|||||||
import { ColumnGroupType, ColumnType } from 'antd/es/table';
|
import { ColumnGroupType, ColumnType } from 'antd/es/table';
|
||||||
import { ColumnsType } from 'antd/lib/table';
|
import { ColumnsType } from 'antd/lib/table';
|
||||||
import logEvent from 'api/common/logEvent';
|
import logEvent from 'api/common/logEvent';
|
||||||
import FacingIssueBtn from 'components/facingIssueBtn/FacingIssueBtn';
|
import LaunchChatSupport from 'components/LaunchChatSupport/LaunchChatSupport';
|
||||||
import { SlidersHorizontal } from 'lucide-react';
|
import { SlidersHorizontal } from 'lucide-react';
|
||||||
import { memo, useEffect, useState } from 'react';
|
import { memo, useEffect, useState } from 'react';
|
||||||
import { popupContainer } from 'utils/selectPopupContainer';
|
import { popupContainer } from 'utils/selectPopupContainer';
|
||||||
@ -96,7 +96,7 @@ function DynamicColumnTable({
|
|||||||
return (
|
return (
|
||||||
<div className="DynamicColumnTable">
|
<div className="DynamicColumnTable">
|
||||||
<Flex justify="flex-end" align="center" gap={8}>
|
<Flex justify="flex-end" align="center" gap={8}>
|
||||||
{facingIssueBtn && <FacingIssueBtn {...facingIssueBtn} />}
|
{facingIssueBtn && <LaunchChatSupport {...facingIssueBtn} />}
|
||||||
{dynamicColumns && (
|
{dynamicColumns && (
|
||||||
<Dropdown
|
<Dropdown
|
||||||
getPopupContainer={popupContainer}
|
getPopupContainer={popupContainer}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
import { TableProps } from 'antd';
|
import { TableProps } from 'antd';
|
||||||
import { ColumnsType } from 'antd/es/table';
|
import { ColumnsType } from 'antd/es/table';
|
||||||
import { ColumnGroupType, ColumnType } from 'antd/lib/table';
|
import { ColumnGroupType, ColumnType } from 'antd/lib/table';
|
||||||
import { FacingIssueBtnProps } from 'components/facingIssueBtn/FacingIssueBtn';
|
import { LaunchChatSupportProps } from 'components/LaunchChatSupport/LaunchChatSupport';
|
||||||
|
|
||||||
import { TableDataSource } from './contants';
|
import { TableDataSource } from './contants';
|
||||||
|
|
||||||
@ -13,7 +13,7 @@ export interface DynamicColumnTableProps extends TableProps<any> {
|
|||||||
tablesource: typeof TableDataSource[keyof typeof TableDataSource];
|
tablesource: typeof TableDataSource[keyof typeof TableDataSource];
|
||||||
dynamicColumns: TableProps<any>['columns'];
|
dynamicColumns: TableProps<any>['columns'];
|
||||||
onDragColumn?: (fromIndex: number, toIndex: number) => void;
|
onDragColumn?: (fromIndex: number, toIndex: number) => void;
|
||||||
facingIssueBtn?: FacingIssueBtnProps;
|
facingIssueBtn?: LaunchChatSupportProps;
|
||||||
shouldSendAlertsLogEvent?: boolean;
|
shouldSendAlertsLogEvent?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,70 +0,0 @@
|
|||||||
import './FacingIssueBtn.style.scss';
|
|
||||||
|
|
||||||
import { Button, Tooltip } from 'antd';
|
|
||||||
import logEvent from 'api/common/logEvent';
|
|
||||||
import cx from 'classnames';
|
|
||||||
import { FeatureKeys } from 'constants/features';
|
|
||||||
import useFeatureFlags from 'hooks/useFeatureFlag';
|
|
||||||
import { defaultTo } from 'lodash-es';
|
|
||||||
import { HelpCircle } from 'lucide-react';
|
|
||||||
import { isCloudUser } from 'utils/app';
|
|
||||||
|
|
||||||
export interface FacingIssueBtnProps {
|
|
||||||
eventName: string;
|
|
||||||
attributes: Record<string, unknown>;
|
|
||||||
message?: string;
|
|
||||||
buttonText?: string;
|
|
||||||
className?: string;
|
|
||||||
onHoverText?: string;
|
|
||||||
intercomMessageDisabled?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
function FacingIssueBtn({
|
|
||||||
attributes,
|
|
||||||
eventName,
|
|
||||||
message = '',
|
|
||||||
buttonText = '',
|
|
||||||
className = '',
|
|
||||||
onHoverText = '',
|
|
||||||
intercomMessageDisabled = false,
|
|
||||||
}: FacingIssueBtnProps): JSX.Element | null {
|
|
||||||
const handleFacingIssuesClick = (): void => {
|
|
||||||
logEvent(eventName, attributes);
|
|
||||||
|
|
||||||
if (window.Intercom && !intercomMessageDisabled) {
|
|
||||||
window.Intercom('showNewMessage', defaultTo(message, ''));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const isChatSupportEnabled = useFeatureFlags(FeatureKeys.CHAT_SUPPORT)?.active;
|
|
||||||
const isCloudUserVal = isCloudUser();
|
|
||||||
|
|
||||||
return isCloudUserVal && isChatSupportEnabled ? ( // Note: we would need to move this condition to license based in future
|
|
||||||
<div className="facing-issue-button">
|
|
||||||
<Tooltip
|
|
||||||
title={onHoverText}
|
|
||||||
autoAdjustOverflow
|
|
||||||
style={{ padding: 8 }}
|
|
||||||
overlayClassName="tooltip-overlay"
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
className={cx('periscope-btn', 'facing-issue-button', className)}
|
|
||||||
onClick={handleFacingIssuesClick}
|
|
||||||
icon={<HelpCircle size={14} />}
|
|
||||||
>
|
|
||||||
{buttonText || 'Facing issues?'}
|
|
||||||
</Button>
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
FacingIssueBtn.defaultProps = {
|
|
||||||
message: '',
|
|
||||||
buttonText: '',
|
|
||||||
className: '',
|
|
||||||
onHoverText: '',
|
|
||||||
intercomMessageDisabled: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default FacingIssueBtn;
|
|
@ -20,4 +20,5 @@ export enum FeatureKeys {
|
|||||||
ONBOARDING = 'ONBOARDING',
|
ONBOARDING = 'ONBOARDING',
|
||||||
CHAT_SUPPORT = 'CHAT_SUPPORT',
|
CHAT_SUPPORT = 'CHAT_SUPPORT',
|
||||||
GATEWAY = 'GATEWAY',
|
GATEWAY = 'GATEWAY',
|
||||||
|
PREMIUM_SUPPORT = 'PREMIUM_SUPPORT',
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,71 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.chat-support-gateway {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 20px;
|
||||||
|
right: 20px;
|
||||||
|
z-index: 1000;
|
||||||
|
|
||||||
|
.chat-support-gateway-btn {
|
||||||
|
max-width: 48px;
|
||||||
|
width: 48px;
|
||||||
|
max-height: 48px;
|
||||||
|
height: 48px;
|
||||||
|
padding: 12px;
|
||||||
|
border-radius: 50%;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
background-color: #f25733;
|
||||||
|
border: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: white !important;
|
||||||
|
border-color: white !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-support-gateway-btn-icon {
|
||||||
|
fill: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-credit-card-btn,
|
||||||
|
.cancel-btn {
|
||||||
|
display: inline-flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlight-text {
|
||||||
|
border-radius: 2px;
|
||||||
|
background: rgba(78, 116, 248, 0.1);
|
||||||
|
padding-right: 4px;
|
||||||
|
font-family: 'Geist Mono';
|
||||||
|
color: var(--bg-robin-500);
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-credit-card-modal {
|
||||||
|
.ant-modal-footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cancel-btn {
|
||||||
|
border-radius: 2px;
|
||||||
|
border: none;
|
||||||
|
background: var(--bg-slate-500, #161922);
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-credit-card-btn {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.isDarkMode {
|
.isDarkMode {
|
||||||
.app-layout {
|
.app-layout {
|
||||||
.app-content {
|
.app-content {
|
||||||
|
@ -9,12 +9,15 @@ import getLocalStorageKey from 'api/browser/localstorage/get';
|
|||||||
import getUserLatestVersion from 'api/user/getLatestVersion';
|
import getUserLatestVersion from 'api/user/getLatestVersion';
|
||||||
import getUserVersion from 'api/user/getVersion';
|
import getUserVersion from 'api/user/getVersion';
|
||||||
import cx from 'classnames';
|
import cx from 'classnames';
|
||||||
|
import ChatSupportGateway from 'components/ChatSupportGateway/ChatSupportGateway';
|
||||||
import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar';
|
import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar';
|
||||||
import { IS_SIDEBAR_COLLAPSED } from 'constants/app';
|
import { IS_SIDEBAR_COLLAPSED } from 'constants/app';
|
||||||
|
import { FeatureKeys } from 'constants/features';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import SideNav from 'container/SideNav';
|
import SideNav from 'container/SideNav';
|
||||||
import TopNav from 'container/TopNav';
|
import TopNav from 'container/TopNav';
|
||||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||||
|
import useFeatureFlags from 'hooks/useFeatureFlag';
|
||||||
import useLicense from 'hooks/useLicense';
|
import useLicense from 'hooks/useLicense';
|
||||||
import { useNotifications } from 'hooks/useNotifications';
|
import { useNotifications } from 'hooks/useNotifications';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
@ -49,6 +52,7 @@ import { getFormattedDate, getRemainingDays } from 'utils/timeUtils';
|
|||||||
import { ChildrenContainer, Layout, LayoutContent } from './styles';
|
import { ChildrenContainer, Layout, LayoutContent } from './styles';
|
||||||
import { getRouteKey } from './utils';
|
import { getRouteKey } from './utils';
|
||||||
|
|
||||||
|
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||||
function AppLayout(props: AppLayoutProps): JSX.Element {
|
function AppLayout(props: AppLayoutProps): JSX.Element {
|
||||||
const { isLoggedIn, user, role } = useSelector<AppState, AppReducer>(
|
const { isLoggedIn, user, role } = useSelector<AppState, AppReducer>(
|
||||||
(state) => state.app,
|
(state) => state.app,
|
||||||
@ -58,10 +62,19 @@ function AppLayout(props: AppLayoutProps): JSX.Element {
|
|||||||
getLocalStorageKey(IS_SIDEBAR_COLLAPSED) === 'true',
|
getLocalStorageKey(IS_SIDEBAR_COLLAPSED) === 'true',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { notifications } = useNotifications();
|
||||||
|
|
||||||
const isDarkMode = useIsDarkMode();
|
const isDarkMode = useIsDarkMode();
|
||||||
|
|
||||||
const { data: licenseData, isFetching } = useLicense();
|
const { data: licenseData, isFetching } = useLicense();
|
||||||
|
|
||||||
|
const isPremiumChatSupportEnabled =
|
||||||
|
useFeatureFlags(FeatureKeys.PREMIUM_SUPPORT)?.active || false;
|
||||||
|
|
||||||
|
const showAddCreditCardModal =
|
||||||
|
!isPremiumChatSupportEnabled &&
|
||||||
|
!licenseData?.payload?.trialConvertedToSubscription;
|
||||||
|
|
||||||
const { pathname } = useLocation();
|
const { pathname } = useLocation();
|
||||||
const { t } = useTranslation(['titles']);
|
const { t } = useTranslation(['titles']);
|
||||||
|
|
||||||
@ -95,8 +108,6 @@ function AppLayout(props: AppLayoutProps): JSX.Element {
|
|||||||
const latestCurrentCounter = useRef(0);
|
const latestCurrentCounter = useRef(0);
|
||||||
const latestVersionCounter = useRef(0);
|
const latestVersionCounter = useRef(0);
|
||||||
|
|
||||||
const { notifications } = useNotifications();
|
|
||||||
|
|
||||||
const onCollapse = useCallback(() => {
|
const onCollapse = useCallback(() => {
|
||||||
setCollapsed((collapsed) => !collapsed);
|
setCollapsed((collapsed) => !collapsed);
|
||||||
}, []);
|
}, []);
|
||||||
@ -331,6 +342,8 @@ function AppLayout(props: AppLayoutProps): JSX.Element {
|
|||||||
</Sentry.ErrorBoundary>
|
</Sentry.ErrorBoundary>
|
||||||
</div>
|
</div>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
|
{showAddCreditCardModal && <ChatSupportGateway />}
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -13,8 +13,8 @@ import {
|
|||||||
import saveAlertApi from 'api/alerts/save';
|
import saveAlertApi from 'api/alerts/save';
|
||||||
import testAlertApi from 'api/alerts/testAlert';
|
import testAlertApi from 'api/alerts/testAlert';
|
||||||
import logEvent from 'api/common/logEvent';
|
import logEvent from 'api/common/logEvent';
|
||||||
import FacingIssueBtn from 'components/facingIssueBtn/FacingIssueBtn';
|
import LaunchChatSupport from 'components/LaunchChatSupport/LaunchChatSupport';
|
||||||
import { alertHelpMessage } from 'components/facingIssueBtn/util';
|
import { alertHelpMessage } from 'components/LaunchChatSupport/util';
|
||||||
import { ALERTS_DATA_SOURCE_MAP } from 'constants/alerts';
|
import { ALERTS_DATA_SOURCE_MAP } from 'constants/alerts';
|
||||||
import { FeatureKeys } from 'constants/features';
|
import { FeatureKeys } from 'constants/features';
|
||||||
import { QueryParams } from 'constants/query';
|
import { QueryParams } from 'constants/query';
|
||||||
@ -712,7 +712,7 @@ function FormAlertRules({
|
|||||||
>
|
>
|
||||||
Check an example alert
|
Check an example alert
|
||||||
</Button>
|
</Button>
|
||||||
<FacingIssueBtn
|
<LaunchChatSupport
|
||||||
attributes={{
|
attributes={{
|
||||||
alert: alertDef?.alert,
|
alert: alertDef?.alert,
|
||||||
alertType: alertDef?.alertType,
|
alertType: alertDef?.alertType,
|
||||||
|
@ -5,7 +5,7 @@ import type { ColumnsType } from 'antd/es/table/interface';
|
|||||||
import saveAlertApi from 'api/alerts/save';
|
import saveAlertApi from 'api/alerts/save';
|
||||||
import logEvent from 'api/common/logEvent';
|
import logEvent from 'api/common/logEvent';
|
||||||
import DropDown from 'components/DropDown/DropDown';
|
import DropDown from 'components/DropDown/DropDown';
|
||||||
import { listAlertMessage } from 'components/facingIssueBtn/util';
|
import { listAlertMessage } from 'components/LaunchChatSupport/util';
|
||||||
import {
|
import {
|
||||||
DynamicColumnsKey,
|
DynamicColumnsKey,
|
||||||
TableDataSource,
|
TableDataSource,
|
||||||
|
@ -25,8 +25,8 @@ import logEvent from 'api/common/logEvent';
|
|||||||
import createDashboard from 'api/dashboard/create';
|
import createDashboard from 'api/dashboard/create';
|
||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
import cx from 'classnames';
|
import cx from 'classnames';
|
||||||
import FacingIssueBtn from 'components/facingIssueBtn/FacingIssueBtn';
|
import LaunchChatSupport from 'components/LaunchChatSupport/LaunchChatSupport';
|
||||||
import { dashboardListMessage } from 'components/facingIssueBtn/util';
|
import { dashboardListMessage } from 'components/LaunchChatSupport/util';
|
||||||
import { ENTITY_VERSION_V4 } from 'constants/app';
|
import { ENTITY_VERSION_V4 } from 'constants/app';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import { Base64Icons } from 'container/NewDashboard/DashboardSettings/General/utils';
|
import { Base64Icons } from 'container/NewDashboard/DashboardSettings/General/utils';
|
||||||
@ -664,7 +664,7 @@ function DashboardsList(): JSX.Element {
|
|||||||
<Typography.Text className="subtitle">
|
<Typography.Text className="subtitle">
|
||||||
Create and manage dashboards for your workspace.
|
Create and manage dashboards for your workspace.
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
<FacingIssueBtn
|
<LaunchChatSupport
|
||||||
attributes={{
|
attributes={{
|
||||||
screen: 'Dashboard list page',
|
screen: 'Dashboard list page',
|
||||||
}}
|
}}
|
||||||
|
@ -12,8 +12,8 @@ import {
|
|||||||
Typography,
|
Typography,
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
import logEvent from 'api/common/logEvent';
|
import logEvent from 'api/common/logEvent';
|
||||||
import FacingIssueBtn from 'components/facingIssueBtn/FacingIssueBtn';
|
import LaunchChatSupport from 'components/LaunchChatSupport/LaunchChatSupport';
|
||||||
import { dashboardHelpMessage } from 'components/facingIssueBtn/util';
|
import { dashboardHelpMessage } from 'components/LaunchChatSupport/util';
|
||||||
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
||||||
import { QueryParams } from 'constants/query';
|
import { QueryParams } from 'constants/query';
|
||||||
import { PANEL_GROUP_TYPES, PANEL_TYPES } from 'constants/queryBuilder';
|
import { PANEL_GROUP_TYPES, PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
@ -322,7 +322,7 @@ function DashboardDescription(props: DashboardDescriptionProps): JSX.Element {
|
|||||||
{isDashboardLocked && <LockKeyhole size={14} />}
|
{isDashboardLocked && <LockKeyhole size={14} />}
|
||||||
</div>
|
</div>
|
||||||
<div className="right-section">
|
<div className="right-section">
|
||||||
<FacingIssueBtn
|
<LaunchChatSupport
|
||||||
attributes={{
|
attributes={{
|
||||||
uuid: selectedDashboard?.uuid,
|
uuid: selectedDashboard?.uuid,
|
||||||
title: updatedTitle,
|
title: updatedTitle,
|
||||||
|
@ -4,7 +4,7 @@ import { Color } from '@signozhq/design-tokens';
|
|||||||
import { Button, Tabs, Tooltip, Typography } from 'antd';
|
import { Button, Tabs, Tooltip, Typography } from 'antd';
|
||||||
import logEvent from 'api/common/logEvent';
|
import logEvent from 'api/common/logEvent';
|
||||||
import PromQLIcon from 'assets/Dashboard/PromQl';
|
import PromQLIcon from 'assets/Dashboard/PromQl';
|
||||||
import FacingIssueBtn from 'components/facingIssueBtn/FacingIssueBtn';
|
import LaunchChatSupport from 'components/LaunchChatSupport/LaunchChatSupport';
|
||||||
import TextToolTip from 'components/TextToolTip';
|
import TextToolTip from 'components/TextToolTip';
|
||||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
import { QBShortcuts } from 'constants/shortcuts/QBShortcuts';
|
import { QBShortcuts } from 'constants/shortcuts/QBShortcuts';
|
||||||
@ -237,7 +237,7 @@ function QuerySection({
|
|||||||
onChange={handleQueryCategoryChange}
|
onChange={handleQueryCategoryChange}
|
||||||
tabBarExtraContent={
|
tabBarExtraContent={
|
||||||
<span style={{ display: 'flex', gap: '1rem', alignItems: 'center' }}>
|
<span style={{ display: 'flex', gap: '1rem', alignItems: 'center' }}>
|
||||||
<FacingIssueBtn
|
<LaunchChatSupport
|
||||||
attributes={{
|
attributes={{
|
||||||
uuid: selectedDashboard?.uuid,
|
uuid: selectedDashboard?.uuid,
|
||||||
title: selectedDashboard?.data.title,
|
title: selectedDashboard?.data.title,
|
||||||
|
@ -2,8 +2,8 @@ import './Integrations.styles.scss';
|
|||||||
|
|
||||||
import { Color } from '@signozhq/design-tokens';
|
import { Color } from '@signozhq/design-tokens';
|
||||||
import { Flex, Input, Typography } from 'antd';
|
import { Flex, Input, Typography } from 'antd';
|
||||||
import FacingIssueBtn from 'components/facingIssueBtn/FacingIssueBtn';
|
import LaunchChatSupport from 'components/LaunchChatSupport/LaunchChatSupport';
|
||||||
import { integrationsListMessage } from 'components/facingIssueBtn/util';
|
import { integrationsListMessage } from 'components/LaunchChatSupport/util';
|
||||||
import { Search } from 'lucide-react';
|
import { Search } from 'lucide-react';
|
||||||
import { Dispatch, SetStateAction } from 'react';
|
import { Dispatch, SetStateAction } from 'react';
|
||||||
|
|
||||||
@ -25,7 +25,7 @@ function Header(props: HeaderProps): JSX.Element {
|
|||||||
<Typography.Text className="subtitle">
|
<Typography.Text className="subtitle">
|
||||||
Manage Integrations for this workspace
|
Manage Integrations for this workspace
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
<FacingIssueBtn
|
<LaunchChatSupport
|
||||||
attributes={{ screen: 'Integrations list page' }}
|
attributes={{ screen: 'Integrations list page' }}
|
||||||
eventName="Integrations: Facing issues in integrations"
|
eventName="Integrations: Facing issues in integrations"
|
||||||
buttonText="Facing issues with integrations"
|
buttonText="Facing issues with integrations"
|
||||||
|
@ -5,8 +5,8 @@ import './IntegrationDetailPage.styles.scss';
|
|||||||
|
|
||||||
import { Color } from '@signozhq/design-tokens';
|
import { Color } from '@signozhq/design-tokens';
|
||||||
import { Button, Flex, Skeleton, Typography } from 'antd';
|
import { Button, Flex, Skeleton, Typography } from 'antd';
|
||||||
import FacingIssueBtn from 'components/facingIssueBtn/FacingIssueBtn';
|
import LaunchChatSupport from 'components/LaunchChatSupport/LaunchChatSupport';
|
||||||
import { integrationDetailMessage } from 'components/facingIssueBtn/util';
|
import { integrationDetailMessage } from 'components/LaunchChatSupport/util';
|
||||||
import { useGetIntegration } from 'hooks/Integrations/useGetIntegration';
|
import { useGetIntegration } from 'hooks/Integrations/useGetIntegration';
|
||||||
import { useGetIntegrationStatus } from 'hooks/Integrations/useGetIntegrationStatus';
|
import { useGetIntegrationStatus } from 'hooks/Integrations/useGetIntegrationStatus';
|
||||||
import { defaultTo } from 'lodash-es';
|
import { defaultTo } from 'lodash-es';
|
||||||
@ -77,7 +77,7 @@ function IntegrationDetailPage(props: IntegrationDetailPageProps): JSX.Element {
|
|||||||
>
|
>
|
||||||
All Integrations
|
All Integrations
|
||||||
</Button>
|
</Button>
|
||||||
<FacingIssueBtn
|
<LaunchChatSupport
|
||||||
attributes={{
|
attributes={{
|
||||||
screen: 'Integrations detail page',
|
screen: 'Integrations detail page',
|
||||||
activeTab: activeDetailTab,
|
activeTab: activeDetailTab,
|
||||||
|
@ -1,17 +1,29 @@
|
|||||||
import './Support.styles.scss';
|
import './Support.styles.scss';
|
||||||
|
|
||||||
import { Button, Card, Typography } from 'antd';
|
import { Button, Card, Modal, Typography } from 'antd';
|
||||||
|
import updateCreditCardApi from 'api/billing/checkout';
|
||||||
import logEvent from 'api/common/logEvent';
|
import logEvent from 'api/common/logEvent';
|
||||||
|
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
||||||
|
import { FeatureKeys } from 'constants/features';
|
||||||
|
import useFeatureFlags from 'hooks/useFeatureFlag';
|
||||||
|
import useLicense from 'hooks/useLicense';
|
||||||
|
import { useNotifications } from 'hooks/useNotifications';
|
||||||
import {
|
import {
|
||||||
Book,
|
Book,
|
||||||
Cable,
|
Cable,
|
||||||
Calendar,
|
Calendar,
|
||||||
|
CreditCard,
|
||||||
Github,
|
Github,
|
||||||
MessageSquare,
|
MessageSquare,
|
||||||
Slack,
|
Slack,
|
||||||
|
X,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { useEffect } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
import { useMutation } from 'react-query';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
|
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||||
|
import { CheckoutSuccessPayloadProps } from 'types/api/billing/checkout';
|
||||||
|
import { License } from 'types/api/licenses/def';
|
||||||
|
|
||||||
const { Title, Text } = Typography;
|
const { Title, Text } = Typography;
|
||||||
|
|
||||||
@ -86,6 +98,12 @@ const supportChannels = [
|
|||||||
|
|
||||||
export default function Support(): JSX.Element {
|
export default function Support(): JSX.Element {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
const { notifications } = useNotifications();
|
||||||
|
const { data: licenseData, isFetching } = useLicense();
|
||||||
|
const [activeLicense, setActiveLicense] = useState<License | null>(null);
|
||||||
|
const [isAddCreditCardModalOpen, setIsAddCreditCardModalOpen] = useState(
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
const handleChannelWithRedirects = (url: string): void => {
|
const handleChannelWithRedirects = (url: string): void => {
|
||||||
window.open(url, '_blank');
|
window.open(url, '_blank');
|
||||||
@ -117,10 +135,67 @@ export default function Support(): JSX.Element {
|
|||||||
window.location.href = mailtoLink;
|
window.location.href = mailtoLink;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isPremiumChatSupportEnabled =
|
||||||
|
useFeatureFlags(FeatureKeys.PREMIUM_SUPPORT)?.active || false;
|
||||||
|
|
||||||
|
const showAddCreditCardModal =
|
||||||
|
!isPremiumChatSupportEnabled &&
|
||||||
|
!licenseData?.payload?.trialConvertedToSubscription;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const activeValidLicense =
|
||||||
|
licenseData?.payload?.licenses?.find(
|
||||||
|
(license) => license.isCurrent === true,
|
||||||
|
) || null;
|
||||||
|
|
||||||
|
setActiveLicense(activeValidLicense);
|
||||||
|
}, [licenseData, isFetching]);
|
||||||
|
|
||||||
|
const handleBillingOnSuccess = (
|
||||||
|
data: ErrorResponse | SuccessResponse<CheckoutSuccessPayloadProps, unknown>,
|
||||||
|
): void => {
|
||||||
|
if (data?.payload?.redirectURL) {
|
||||||
|
const newTab = document.createElement('a');
|
||||||
|
newTab.href = data.payload.redirectURL;
|
||||||
|
newTab.target = '_blank';
|
||||||
|
newTab.rel = 'noopener noreferrer';
|
||||||
|
newTab.click();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleBillingOnError = (): void => {
|
||||||
|
notifications.error({
|
||||||
|
message: SOMETHING_WENT_WRONG,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const { mutate: updateCreditCard, isLoading: isLoadingBilling } = useMutation(
|
||||||
|
updateCreditCardApi,
|
||||||
|
{
|
||||||
|
onSuccess: (data) => {
|
||||||
|
handleBillingOnSuccess(data);
|
||||||
|
},
|
||||||
|
onError: handleBillingOnError,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleAddCreditCard = (): void => {
|
||||||
|
logEvent('Add Credit card modal: Clicked', {
|
||||||
|
source: `chat`,
|
||||||
|
page: 'support',
|
||||||
|
});
|
||||||
|
|
||||||
|
updateCreditCard({
|
||||||
|
licenseKey: activeLicense?.key || '',
|
||||||
|
successURL: window.location.href,
|
||||||
|
cancelURL: window.location.href,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const handleChat = (): void => {
|
const handleChat = (): void => {
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
if (showAddCreditCardModal) {
|
||||||
// @ts-ignore
|
setIsAddCreditCardModalOpen(true);
|
||||||
if (window.Intercom) {
|
} else if (window.Intercom) {
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
window.Intercom('show');
|
window.Intercom('show');
|
||||||
@ -183,6 +258,43 @@ export default function Support(): JSX.Element {
|
|||||||
),
|
),
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Add Credit Card Modal */}
|
||||||
|
<Modal
|
||||||
|
className="add-credit-card-modal"
|
||||||
|
title={<span className="title">Add Credit Card for Chat Support</span>}
|
||||||
|
open={isAddCreditCardModalOpen}
|
||||||
|
closable
|
||||||
|
onCancel={(): void => setIsAddCreditCardModalOpen(false)}
|
||||||
|
destroyOnClose
|
||||||
|
footer={[
|
||||||
|
<Button
|
||||||
|
key="cancel"
|
||||||
|
onClick={(): void => setIsAddCreditCardModalOpen(false)}
|
||||||
|
className="cancel-btn"
|
||||||
|
icon={<X size={16} />}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>,
|
||||||
|
<Button
|
||||||
|
key="submit"
|
||||||
|
type="primary"
|
||||||
|
icon={<CreditCard size={16} />}
|
||||||
|
size="middle"
|
||||||
|
loading={isLoadingBilling}
|
||||||
|
disabled={isLoadingBilling}
|
||||||
|
onClick={handleAddCreditCard}
|
||||||
|
className="add-credit-card-btn"
|
||||||
|
>
|
||||||
|
Add Credit Card
|
||||||
|
</Button>,
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Typography.Text className="add-credit-card-text">
|
||||||
|
You're currently on <span className="highlight-text">Trial plan</span>
|
||||||
|
. Add a credit card to access SigNoz chat support to your workspace.
|
||||||
|
</Typography.Text>
|
||||||
|
</Modal>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user