feat: add trial status badge in appbar

This commit is contained in:
Ali BARIN
2023-04-08 10:10:51 +00:00
parent 94a3b66130
commit d4c542168c
9 changed files with 132 additions and 7 deletions

View File

@@ -9,7 +9,9 @@ const getTrialStatus = async (
if (!appConfig.isCloud) return; if (!appConfig.isCloud) return;
const inTrial = await context.currentUser.inTrial(); const inTrial = await context.currentUser.inTrial();
if (!inTrial) return; const hasActiveSubscription = await context.currentUser.hasActiveSubscription();
if (!inTrial && hasActiveSubscription) return;
return { return {
expireAt: context.currentUser.trialExpiryDate, expireAt: context.currentUser.trialExpiryDate,

View File

@@ -165,6 +165,16 @@ class User extends Base {
this.trialExpiryDate = DateTime.now().plus({ days: 30 }).toISODate(); this.trialExpiryDate = DateTime.now().plus({ days: 30 }).toISODate();
} }
async hasActiveSubscription() {
if (!appConfig.isCloud) {
return false;
}
const subscription = await this.$relatedQuery('currentSubscription');
return subscription?.isActive;
}
async inTrial() { async inTrial() {
if (!appConfig.isCloud) { if (!appConfig.isCloud) {
return false; return false;
@@ -174,9 +184,7 @@ class User extends Base {
return false; return false;
} }
const subscription = await this.$relatedQuery('currentSubscription'); if (await this.hasActiveSubscription()) {
if (subscription?.isActive) {
return false; return false;
} }

View File

@@ -12,6 +12,7 @@ import AccountCircleIcon from '@mui/icons-material/AccountCircle';
import * as URLS from 'config/urls'; import * as URLS from 'config/urls';
import AccountDropdownMenu from 'components/AccountDropdownMenu'; import AccountDropdownMenu from 'components/AccountDropdownMenu';
import TrialStatusBadge from 'components/TrialStatusBadge/index.ee';
import Container from 'components/Container'; import Container from 'components/Container';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import { Link } from './style'; import { Link } from './style';
@@ -67,9 +68,10 @@ export default function AppBar(props: AppBarProps): React.ReactElement {
</Link> </Link>
</div> </div>
<TrialStatusBadge />
<IconButton <IconButton
size="large" size="large"
edge="start"
color="inherit" color="inherit"
onClick={handleAccountMenuOpen} onClick={handleAccountMenuOpen}
aria-controls={accountMenuId} aria-controls={accountMenuId}

View File

@@ -0,0 +1,24 @@
import * as React from 'react';
import { Link } from 'react-router-dom';
import { Chip } from './style.ee';
import * as URLS from 'config/urls';
import useTrialStatus from 'hooks/useTrialStatus.ee';
export default function TrialStatusBadge(): React.ReactElement {
const data = useTrialStatus();
if (!data) return <React.Fragment />;
const { message, status } = data;
return (
<Chip
component={Link}
to={URLS.SETTINGS_BILLING_AND_USAGE}
clickable
label={message}
color={status}
/>
);
}

View File

@@ -0,0 +1,13 @@
import { styled } from '@mui/material/styles';
import MuiChip, { chipClasses } from '@mui/material/Chip';
export const Chip = styled(MuiChip)`
&.${chipClasses.root} {
font-weight: 500;
}
&.${chipClasses.colorWarning} {
background: #fef3c7;
color: #78350f;
}
` as typeof MuiChip;

View File

@@ -0,0 +1,9 @@
import { gql } from '@apollo/client';
export const GET_TRIAL_STATUS = gql`
query GetTrialStatus {
getTrialStatus {
expireAt
}
}
`;

View File

@@ -23,7 +23,7 @@ function transform(billingAndUsageData: NonNullable<UseBillingAndUsageDataReturn
} }
type UseBillingAndUsageDataReturn = { type UseBillingAndUsageDataReturn = {
subscription: TSubscription, subscription: TSubscription;
usage: { usage: {
task: number; task: number;
} }

View File

@@ -0,0 +1,64 @@
import { useQuery } from '@apollo/client';
import { DateTime } from 'luxon';
import { GET_TRIAL_STATUS } from 'graphql/queries/get-trial-status.ee';
import useFormatMessage from './useFormatMessage';
type UseTrialStatusReturn = {
expireAt: DateTime;
message: string;
status: 'error' | 'warning';
} | null;
function getDiffInDays(date: DateTime) {
const today = DateTime.now().startOf('day');
const diffInDays = date.diff(today, 'days').days;
const roundedDiffInDays = Math.round(diffInDays);
return roundedDiffInDays;
}
function getFeedbackPayload(date: DateTime) {
const diffInDays = getDiffInDays(date);
if (diffInDays <= -1) {
return {
translationEntryId: 'trialBadge.over',
status: 'error' as const,
};
} else if (diffInDays <= 0) {
return {
translationEntryId: 'trialBadge.endsToday',
status: 'warning' as const,
}
} else {
return {
translationEntryId: 'trialBadge.xDaysLeft',
translationEntryValues: {
remainingDays: diffInDays
},
status: 'warning' as const,
}
}
}
export default function useTrialStatus(): UseTrialStatusReturn {
const formatMessage = useFormatMessage();
const { data, loading } = useQuery(GET_TRIAL_STATUS);
if (loading || !data.getTrialStatus) return null;
const expireAt = DateTime.fromMillis(Number(data.getTrialStatus.expireAt)).startOf('day');
const {
translationEntryId,
translationEntryValues,
status,
} = getFeedbackPayload(expireAt);
return {
message: formatMessage(translationEntryId, translationEntryValues),
expireAt,
status
};
}

View File

@@ -152,5 +152,8 @@
"invoices.date": "Date", "invoices.date": "Date",
"invoices.amount": "Amount", "invoices.amount": "Amount",
"invoices.invoice": "Invoice", "invoices.invoice": "Invoice",
"invoices.link": "Link" "invoices.link": "Link",
"trialBadge.xDaysLeft": "{remainingDays} trial {remainingDays, plural, one {day} other {days}} left",
"trialBadge.endsToday": "Trial ends today",
"trialBadge.over": "Trial is over"
} }