mirror of
https://github.com/SigNoz/signoz.git
synced 2025-12-18 07:56:56 +00:00
feat(license): show refetch payment status button to reconcile payments (#8551)
This commit is contained in:
parent
ebb2f1fd63
commit
478d28eda1
@ -8,5 +8,6 @@
|
|||||||
"actNow": "Act now to avoid any disruptions and continue where you left off.",
|
"actNow": "Act now to avoid any disruptions and continue where you left off.",
|
||||||
"contactAdmin": "Contact your admin to proceed with the upgrade.",
|
"contactAdmin": "Contact your admin to proceed with the upgrade.",
|
||||||
"continueMyJourney": "Settle your bill to continue",
|
"continueMyJourney": "Settle your bill to continue",
|
||||||
"somethingWentWrong": "Something went wrong"
|
"somethingWentWrong": "Something went wrong",
|
||||||
|
"refreshPaymentStatus": "Refresh Status"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,5 +8,6 @@
|
|||||||
"actNow": "Act now to avoid any disruptions and continue where you left off.",
|
"actNow": "Act now to avoid any disruptions and continue where you left off.",
|
||||||
"contactAdmin": "Contact your admin to proceed with the upgrade.",
|
"contactAdmin": "Contact your admin to proceed with the upgrade.",
|
||||||
"continueMyJourney": "Settle your bill to continue",
|
"continueMyJourney": "Settle your bill to continue",
|
||||||
"somethingWentWrong": "Something went wrong"
|
"somethingWentWrong": "Something went wrong",
|
||||||
|
"refreshPaymentStatus": "Refresh Status"
|
||||||
}
|
}
|
||||||
|
|||||||
24
frontend/src/api/v3/licenses/post.ts
Normal file
24
frontend/src/api/v3/licenses/post.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { ApiV3Instance as axios } from 'api';
|
||||||
|
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
||||||
|
import { AxiosError } from 'axios';
|
||||||
|
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||||
|
import { PayloadProps, Props } from 'types/api/licenses/apply';
|
||||||
|
|
||||||
|
const apply = async (
|
||||||
|
props: Props,
|
||||||
|
): Promise<SuccessResponseV2<PayloadProps>> => {
|
||||||
|
try {
|
||||||
|
const response = await axios.post<PayloadProps>('/licenses', {
|
||||||
|
key: props.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
httpStatusCode: response.status,
|
||||||
|
data: response.data,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
ErrorResponseHandlerV2(error as AxiosError<ErrorV2Resp>);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default apply;
|
||||||
@ -2,15 +2,11 @@ import { ApiV3Instance as axios } from 'api';
|
|||||||
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||||
import { PayloadProps, Props } from 'types/api/licenses/apply';
|
import { PayloadProps } from 'types/api/licenses/apply';
|
||||||
|
|
||||||
const apply = async (
|
const apply = async (): Promise<SuccessResponseV2<PayloadProps>> => {
|
||||||
props: Props,
|
|
||||||
): Promise<SuccessResponseV2<PayloadProps>> => {
|
|
||||||
try {
|
try {
|
||||||
const response = await axios.post<PayloadProps>('/licenses', {
|
const response = await axios.put<PayloadProps>('/licenses');
|
||||||
key: props.key,
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
httpStatusCode: response.status,
|
httpStatusCode: response.status,
|
||||||
|
|||||||
@ -0,0 +1,56 @@
|
|||||||
|
import { Button, Tooltip } from 'antd';
|
||||||
|
import refreshPaymentStatus from 'api/v3/licenses/put';
|
||||||
|
import cx from 'classnames';
|
||||||
|
import { RefreshCcw } from 'lucide-react';
|
||||||
|
import { useAppContext } from 'providers/App/App';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
function RefreshPaymentStatus({
|
||||||
|
btnShape,
|
||||||
|
type,
|
||||||
|
}: {
|
||||||
|
btnShape?: 'default' | 'round' | 'circle';
|
||||||
|
type?: 'button' | 'text' | 'tooltip';
|
||||||
|
}): JSX.Element {
|
||||||
|
const { t } = useTranslation(['failedPayment']);
|
||||||
|
const { activeLicenseRefetch } = useAppContext();
|
||||||
|
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
|
const handleRefreshPaymentStatus = async (): Promise<void> => {
|
||||||
|
setIsLoading(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await refreshPaymentStatus();
|
||||||
|
|
||||||
|
await Promise.all([activeLicenseRefetch()]);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
setIsLoading(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span className="refresh-payment-status-btn-wrapper">
|
||||||
|
<Tooltip title={type === 'tooltip' ? t('refreshPaymentStatus') : ''}>
|
||||||
|
<Button
|
||||||
|
type={type === 'text' ? 'text' : 'default'}
|
||||||
|
shape={btnShape}
|
||||||
|
className={cx('periscope-btn', { text: type === 'text' })}
|
||||||
|
onClick={handleRefreshPaymentStatus}
|
||||||
|
icon={<RefreshCcw size={14} />}
|
||||||
|
loading={isLoading}
|
||||||
|
>
|
||||||
|
{type !== 'tooltip' ? t('refreshPaymentStatus') : ''}
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
RefreshPaymentStatus.defaultProps = {
|
||||||
|
btnShape: 'default',
|
||||||
|
type: 'button',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RefreshPaymentStatus;
|
||||||
@ -4,6 +4,21 @@
|
|||||||
.app-banner-wrapper {
|
.app-banner-wrapper {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
|
.refresh-payment-status {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
margin-left: 4px;
|
||||||
|
|
||||||
|
.refresh-payment-status-btn-wrapper {
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-layout {
|
.app-layout {
|
||||||
@ -12,24 +27,24 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
&.isWorkspaceRestricted {
|
&.isWorkspaceRestricted {
|
||||||
height: calc(100% - 32px);
|
height: calc(100% - 48px);
|
||||||
|
|
||||||
// same styles as its either trial expired or payment failed
|
// same styles as its either trial expired or payment failed
|
||||||
&.isTrialExpired {
|
&.isTrialExpired {
|
||||||
height: calc(100% - 64px);
|
height: calc(100% - 96px);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.isPaymentFailed {
|
&.isPaymentFailed {
|
||||||
height: calc(100% - 64px);
|
height: calc(100% - 96px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.isTrialExpired {
|
&.isTrialExpired {
|
||||||
height: calc(100% - 32px);
|
height: calc(100% - 48px);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.isPaymentFailed {
|
&.isPaymentFailed {
|
||||||
height: calc(100% - 32px);
|
height: calc(100% - 48px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-content {
|
.app-content {
|
||||||
@ -196,5 +211,5 @@
|
|||||||
.workspace-restricted-banner,
|
.workspace-restricted-banner,
|
||||||
.trial-expiry-banner,
|
.trial-expiry-banner,
|
||||||
.payment-failed-banner {
|
.payment-failed-banner {
|
||||||
height: 32px;
|
height: 48px;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import cx from 'classnames';
|
|||||||
import ChangelogModal from 'components/ChangelogModal/ChangelogModal';
|
import ChangelogModal from 'components/ChangelogModal/ChangelogModal';
|
||||||
import ChatSupportGateway from 'components/ChatSupportGateway/ChatSupportGateway';
|
import ChatSupportGateway from 'components/ChatSupportGateway/ChatSupportGateway';
|
||||||
import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar';
|
import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar';
|
||||||
|
import RefreshPaymentStatus from 'components/RefreshPaymentStatus/RefreshPaymentStatus';
|
||||||
import { Events } from 'constants/events';
|
import { Events } from 'constants/events';
|
||||||
import { FeatureKeys } from 'constants/features';
|
import { FeatureKeys } from 'constants/features';
|
||||||
import { LOCALSTORAGE } from 'constants/localStorage';
|
import { LOCALSTORAGE } from 'constants/localStorage';
|
||||||
@ -181,11 +182,11 @@ function AppLayout(props: AppLayoutProps): JSX.Element {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// refetch the changelog only when the current tab becomes active + there isn't an active request + no changelog already available
|
// refetch the changelog only when the current tab becomes active + there isn't an active request
|
||||||
if (!changelog && !getChangelogByVersionResponse.isLoading && isVisible) {
|
if (!getChangelogByVersionResponse.isLoading && isVisible) {
|
||||||
getChangelogByVersionResponse.refetch();
|
getChangelogByVersionResponse.refetch();
|
||||||
}
|
}
|
||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [isVisible]);
|
}, [isVisible]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -665,6 +666,10 @@ function AppLayout(props: AppLayoutProps): JSX.Element {
|
|||||||
upgrade
|
upgrade
|
||||||
</a>
|
</a>
|
||||||
to continue using SigNoz features.
|
to continue using SigNoz features.
|
||||||
|
<span className="refresh-payment-status">
|
||||||
|
{' '}
|
||||||
|
| Already upgraded? <RefreshPaymentStatus type="text" />
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
'Please contact your administrator for upgrading to a paid plan.'
|
'Please contact your administrator for upgrading to a paid plan.'
|
||||||
@ -691,6 +696,10 @@ function AppLayout(props: AppLayoutProps): JSX.Element {
|
|||||||
pay the bill
|
pay the bill
|
||||||
</a>
|
</a>
|
||||||
to continue using SigNoz features.
|
to continue using SigNoz features.
|
||||||
|
<span className="refresh-payment-status">
|
||||||
|
{' '}
|
||||||
|
| Already paid? <RefreshPaymentStatus type="text" />
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
' Please contact your administrator to pay the bill.'
|
' Please contact your administrator to pay the bill.'
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import getUsage, { UsageResponsePayloadProps } from 'api/billing/getUsage';
|
|||||||
import logEvent from 'api/common/logEvent';
|
import logEvent from 'api/common/logEvent';
|
||||||
import updateCreditCardApi from 'api/v1/checkout/create';
|
import updateCreditCardApi from 'api/v1/checkout/create';
|
||||||
import manageCreditCardApi from 'api/v1/portal/create';
|
import manageCreditCardApi from 'api/v1/portal/create';
|
||||||
|
import RefreshPaymentStatus from 'components/RefreshPaymentStatus/RefreshPaymentStatus';
|
||||||
import Spinner from 'components/Spinner';
|
import Spinner from 'components/Spinner';
|
||||||
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
||||||
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
|
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
|
||||||
@ -440,14 +441,15 @@ export default function BillingContainer(): JSX.Element {
|
|||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
) : null}
|
) : null}
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex gap={20}>
|
<Flex gap={8}>
|
||||||
<Button
|
<Button
|
||||||
type="dashed"
|
type="default"
|
||||||
size="middle"
|
size="middle"
|
||||||
loading={isLoadingBilling || isLoadingManageBilling}
|
loading={isLoadingBilling || isLoadingManageBilling}
|
||||||
disabled={isLoading || isFetchingBillingData}
|
disabled={isLoading || isFetchingBillingData}
|
||||||
onClick={handleCsvDownload}
|
onClick={handleCsvDownload}
|
||||||
icon={<CloudDownloadOutlined />}
|
icon={<CloudDownloadOutlined />}
|
||||||
|
className="periscope-btn"
|
||||||
>
|
>
|
||||||
Download CSV
|
Download CSV
|
||||||
</Button>
|
</Button>
|
||||||
@ -463,6 +465,8 @@ export default function BillingContainer(): JSX.Element {
|
|||||||
? t('manage_billing')
|
? t('manage_billing')
|
||||||
: t('upgrade_plan')}
|
: t('upgrade_plan')}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
|
<RefreshPaymentStatus type="tooltip" />
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { Button, Form, Input } from 'antd';
|
import { Button, Form, Input } from 'antd';
|
||||||
import apply from 'api/v3/licenses/put';
|
import apply from 'api/v3/licenses/post';
|
||||||
import { useNotifications } from 'hooks/useNotifications';
|
import { useNotifications } from 'hooks/useNotifications';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|||||||
@ -48,7 +48,7 @@ $dark-theme: 'darkMode';
|
|||||||
&__actions {
|
&__actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 16px;
|
gap: 8px;
|
||||||
|
|
||||||
.ant-btn-link {
|
.ant-btn-link {
|
||||||
color: var(--text-vanilla-400);
|
color: var(--text-vanilla-400);
|
||||||
|
|||||||
@ -24,9 +24,6 @@ describe('WorkspaceLocked', () => {
|
|||||||
});
|
});
|
||||||
expect(workspaceLocked).toBeInTheDocument();
|
expect(workspaceLocked).toBeInTheDocument();
|
||||||
|
|
||||||
const gotQuestionText = await screen.findByText(/got question?/i);
|
|
||||||
expect(gotQuestionText).toBeInTheDocument();
|
|
||||||
|
|
||||||
const contactUsBtn = await screen.findByRole('button', {
|
const contactUsBtn = await screen.findByRole('button', {
|
||||||
name: /Contact Us/i,
|
name: /Contact Us/i,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -18,6 +18,7 @@ import {
|
|||||||
} from 'antd';
|
} from 'antd';
|
||||||
import logEvent from 'api/common/logEvent';
|
import logEvent from 'api/common/logEvent';
|
||||||
import updateCreditCardApi from 'api/v1/checkout/create';
|
import updateCreditCardApi from 'api/v1/checkout/create';
|
||||||
|
import RefreshPaymentStatus from 'components/RefreshPaymentStatus/RefreshPaymentStatus';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import { useNotifications } from 'hooks/useNotifications';
|
import { useNotifications } from 'hooks/useNotifications';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
@ -289,26 +290,28 @@ export default function WorkspaceBlocked(): JSX.Element {
|
|||||||
</span>
|
</span>
|
||||||
<span className="workspace-locked__modal__header__actions">
|
<span className="workspace-locked__modal__header__actions">
|
||||||
{isAdmin && (
|
{isAdmin && (
|
||||||
<Button
|
<Flex gap={8} justify="center" align="center">
|
||||||
className="workspace-locked__modal__header__actions__billing"
|
<Button
|
||||||
type="link"
|
className="workspace-locked__modal__header__actions__billing"
|
||||||
size="small"
|
type="link"
|
||||||
role="button"
|
size="small"
|
||||||
onClick={handleViewBilling}
|
role="button"
|
||||||
>
|
onClick={handleViewBilling}
|
||||||
View Billing
|
>
|
||||||
</Button>
|
View Billing
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<RefreshPaymentStatus btnShape="round" />
|
||||||
|
</Flex>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Typography.Text className="workspace-locked__modal__title">
|
|
||||||
Got Questions?
|
|
||||||
</Typography.Text>
|
|
||||||
<Button
|
<Button
|
||||||
type="default"
|
type="default"
|
||||||
shape="round"
|
shape="round"
|
||||||
size="middle"
|
size="middle"
|
||||||
href="mailto:cloud-support@signoz.io"
|
href="mailto:cloud-support@signoz.io"
|
||||||
role="button"
|
role="button"
|
||||||
|
className="periscope-btn"
|
||||||
onClick={handleContactUsClick}
|
onClick={handleContactUsClick}
|
||||||
>
|
>
|
||||||
Contact Us
|
Contact Us
|
||||||
@ -349,7 +352,7 @@ export default function WorkspaceBlocked(): JSX.Element {
|
|||||||
justify="center"
|
justify="center"
|
||||||
align="middle"
|
align="middle"
|
||||||
className="workspace-locked__modal__cta"
|
className="workspace-locked__modal__cta"
|
||||||
gutter={[16, 16]}
|
gutter={[8, 8]}
|
||||||
>
|
>
|
||||||
<Col>
|
<Col>
|
||||||
<Alert
|
<Alert
|
||||||
@ -360,34 +363,37 @@ export default function WorkspaceBlocked(): JSX.Element {
|
|||||||
</Row>
|
</Row>
|
||||||
)}
|
)}
|
||||||
{isAdmin && (
|
{isAdmin && (
|
||||||
<Row
|
<Flex gap={8} vertical justify="center" align="center">
|
||||||
justify="center"
|
<Row
|
||||||
align="middle"
|
justify="center"
|
||||||
className="workspace-locked__modal__cta"
|
align="middle"
|
||||||
gutter={[16, 16]}
|
className="workspace-locked__modal__cta"
|
||||||
>
|
gutter={[8, 8]}
|
||||||
<Col>
|
>
|
||||||
<Button
|
<Col>
|
||||||
type="primary"
|
<Button
|
||||||
shape="round"
|
type="primary"
|
||||||
size="middle"
|
shape="round"
|
||||||
loading={isLoading}
|
size="middle"
|
||||||
onClick={handleUpdateCreditCard}
|
loading={isLoading}
|
||||||
>
|
onClick={handleUpdateCreditCard}
|
||||||
Continue my Journey
|
>
|
||||||
</Button>
|
Continue my Journey
|
||||||
</Col>
|
</Button>
|
||||||
<Col>
|
</Col>
|
||||||
<Button
|
<Col>
|
||||||
type="default"
|
<Button
|
||||||
shape="round"
|
type="default"
|
||||||
size="middle"
|
shape="round"
|
||||||
onClick={handleExtendTrial}
|
size="middle"
|
||||||
>
|
className="periscope-btn"
|
||||||
{t('needMoreTime')}
|
onClick={handleExtendTrial}
|
||||||
</Button>
|
>
|
||||||
</Col>
|
{t('needMoreTime')}
|
||||||
</Row>
|
</Button>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Flex>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="workspace-locked__tabs">
|
<div className="workspace-locked__tabs">
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import {
|
|||||||
Alert,
|
Alert,
|
||||||
Button,
|
Button,
|
||||||
Col,
|
Col,
|
||||||
|
Flex,
|
||||||
Modal,
|
Modal,
|
||||||
Row,
|
Row,
|
||||||
Skeleton,
|
Skeleton,
|
||||||
@ -11,6 +12,7 @@ import {
|
|||||||
Typography,
|
Typography,
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
import manageCreditCardApi from 'api/v1/portal/create';
|
import manageCreditCardApi from 'api/v1/portal/create';
|
||||||
|
import RefreshPaymentStatus from 'components/RefreshPaymentStatus/RefreshPaymentStatus';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { useNotifications } from 'hooks/useNotifications';
|
import { useNotifications } from 'hooks/useNotifications';
|
||||||
@ -146,9 +148,9 @@ function WorkspaceSuspended(): JSX.Element {
|
|||||||
justify="center"
|
justify="center"
|
||||||
align="middle"
|
align="middle"
|
||||||
className="workspace-suspended__modal__cta"
|
className="workspace-suspended__modal__cta"
|
||||||
gutter={[16, 16]}
|
gutter={[8, 8]}
|
||||||
>
|
>
|
||||||
<Col>
|
<Flex gap={8} justify="center" align="center">
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
shape="round"
|
shape="round"
|
||||||
@ -158,7 +160,8 @@ function WorkspaceSuspended(): JSX.Element {
|
|||||||
>
|
>
|
||||||
{t('continueMyJourney')}
|
{t('continueMyJourney')}
|
||||||
</Button>
|
</Button>
|
||||||
</Col>
|
<RefreshPaymentStatus btnShape="round" />
|
||||||
|
</Flex>
|
||||||
</Row>
|
</Row>
|
||||||
)}
|
)}
|
||||||
<div className="workspace-suspended__creative">
|
<div className="workspace-suspended__creative">
|
||||||
|
|||||||
@ -54,6 +54,20 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.text {
|
||||||
|
color: var(--bg-vanilla-100) !important;
|
||||||
|
background-color: transparent !important;
|
||||||
|
border: none;
|
||||||
|
box-shadow: none;
|
||||||
|
box-shadow: none;
|
||||||
|
padding: 4px 4px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--bg-vanilla-300) !important;
|
||||||
|
background-color: transparent !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&.success {
|
&.success {
|
||||||
color: var(--bg-forest-400) !important;
|
color: var(--bg-forest-400) !important;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user