diff --git a/static/gsApp/views/subscriptionPage/components/categoryUsageDrawer.tsx b/static/gsApp/views/subscriptionPage/components/categoryUsageDrawer.tsx index c240cd0d961f0b..5ebd579ca36644 100644 --- a/static/gsApp/views/subscriptionPage/components/categoryUsageDrawer.tsx +++ b/static/gsApp/views/subscriptionPage/components/categoryUsageDrawer.tsx @@ -1,110 +1,31 @@ -import OptionSelector from 'sentry/components/charts/optionSelector'; -import {ChartControls, InlineContainer} from 'sentry/components/charts/styles'; import {Container, Flex} from 'sentry/components/core/layout'; import {DrawerBody, DrawerHeader} from 'sentry/components/globalDrawer/components'; -import {t} from 'sentry/locale'; -import {DataCategory} from 'sentry/types/core'; -import {useLocation} from 'sentry/utils/useLocation'; -import {useNavigate} from 'sentry/utils/useNavigate'; import useOrganization from 'sentry/utils/useOrganization'; -import {CHART_OPTIONS_DATA_TRANSFORM} from 'sentry/views/organizationStats/usageChart'; import {useProductBillingMetadata} from 'getsentry/hooks/useProductBillingMetadata'; import { - PlanTier, type BillingMetricHistory, - type BillingStats, - type BillingStatTotal, + type CustomerUsage, type Subscription, } from 'getsentry/types'; -import {addBillingStatTotals, isAm2Plan} from 'getsentry/utils/billing'; -import { - getChunkCategoryFromDuration, - isContinuousProfiling, -} from 'getsentry/utils/dataCategory'; -import trackGetsentryAnalytics from 'getsentry/utils/trackGetsentryAnalytics'; -import { - ProductUsageChart, - selectedTransform, -} from 'getsentry/views/subscriptionPage/reservedUsageChart'; -import {EMPTY_STAT_TOTAL} from 'getsentry/views/subscriptionPage/usageTotals'; -import UsageTotalsTable from 'getsentry/views/subscriptionPage/usageTotalsTable'; +import UsageCharts from 'getsentry/views/subscriptionPage/usageOverview/charts'; interface CategoryUsageDrawerProps { categoryInfo: BillingMetricHistory; - eventTotals: Record; - periodEnd: string; - periodStart: string; - stats: BillingStats; subscription: Subscription; - totals: BillingStatTotal; + usageData: CustomerUsage; } function CategoryUsageDrawer({ categoryInfo, - stats, - totals, - eventTotals, subscription, - periodStart, - periodEnd, + usageData, }: CategoryUsageDrawerProps) { const organization = useOrganization(); - const navigate = useNavigate(); - const location = useLocation(); - const transform = selectedTransform(location); - const {category, usage: billedUsage} = categoryInfo; + const {category} = categoryInfo; // XXX(isabella): using this to make knip happy til the hook is used in other places const {displayName} = useProductBillingMetadata(subscription, category); - const usageStats = { - [category]: stats, - }; - - const adjustedTotals = isContinuousProfiling(category) - ? { - ...addBillingStatTotals(totals, [ - eventTotals[getChunkCategoryFromDuration(category)] ?? EMPTY_STAT_TOTAL, - !isAm2Plan(subscription.plan) && category === DataCategory.PROFILE_DURATION - ? (eventTotals[DataCategory.PROFILES] ?? EMPTY_STAT_TOTAL) - : EMPTY_STAT_TOTAL, - ]), - accepted: billedUsage, - } - : {...totals, accepted: billedUsage}; - - const renderFooter = () => { - return ( - - - { - trackGetsentryAnalytics( - 'subscription_page.usage_overview.transform_changed', - { - organization, - subscription, - transform: val, - } - ); - navigate({ - pathname: location.pathname, - query: {...location.query, transform: val}, - }); - }} - /> - - - ); - }; - - const showEventBreakdown = - organization.features.includes('profiling-billing') && - subscription.planTier === PlanTier.AM2 && - category === DataCategory.TRANSACTIONS; return ( @@ -112,39 +33,12 @@ function CategoryUsageDrawer({ {displayName} - - - - {showEventBreakdown && - Object.entries(eventTotals).map(([key, eventTotal]) => { - return ( - - ); - })} - ); diff --git a/static/gsApp/views/subscriptionPage/components/categoryUsageDrawer.spec.tsx b/static/gsApp/views/subscriptionPage/usageOverview/charts.spec.tsx similarity index 76% rename from static/gsApp/views/subscriptionPage/components/categoryUsageDrawer.spec.tsx rename to static/gsApp/views/subscriptionPage/usageOverview/charts.spec.tsx index b7e0754669f0d4..0a61eb83f632a6 100644 --- a/static/gsApp/views/subscriptionPage/components/categoryUsageDrawer.spec.tsx +++ b/static/gsApp/views/subscriptionPage/usageOverview/charts.spec.tsx @@ -1,6 +1,7 @@ import {OrganizationFixture} from 'sentry-fixture/organization'; import {BillingStatFixture} from 'getsentry-test/fixtures/billingStat'; +import {CustomerUsageFixture} from 'getsentry-test/fixtures/customerUsage'; import {SubscriptionFixture} from 'getsentry-test/fixtures/subscription'; import {UsageTotalFixture} from 'getsentry-test/fixtures/usageTotal'; import {act, render, screen} from 'sentry-test/reactTestingLibrary'; @@ -10,10 +11,10 @@ import {OrganizationContext} from 'sentry/views/organizationContext'; import SubscriptionStore from 'getsentry/stores/subscriptionStore'; import {PlanTier} from 'getsentry/types'; +import UsageCharts from 'getsentry/views/subscriptionPage/usageOverview/charts'; +import type {BreakdownPanelProps} from 'getsentry/views/subscriptionPage/usageOverview/types'; -import CategoryUsageDrawer from './categoryUsageDrawer'; - -describe('CategoryUsageDrawer', () => { +describe('UsageCharts', () => { const organization = OrganizationFixture(); const totals = UsageTotalFixture({ accepted: 50, @@ -28,10 +29,10 @@ describe('CategoryUsageDrawer', () => { organization.features.push('subscriptions-v3'); }); - function renderComponent(props: any) { + function renderComponent(props: Omit) { return render( - + ); } @@ -45,15 +46,19 @@ describe('CategoryUsageDrawer', () => { usage: 50, }; SubscriptionStore.set(organization.slug, subscription); + const usageData = CustomerUsageFixture({ + totals: { + [DataCategory.ERRORS]: totals, + }, + stats: { + [DataCategory.ERRORS]: stats, + }, + }); await act(async () => { renderComponent({ subscription, - categoryInfo: subscription.categories.errors, - eventTotals: {[DataCategory.ERRORS]: totals}, - totals, - stats, - periodEnd: '2021-02-01', - periodStart: '2021-01-01', + usageData, + selectedProduct: DataCategory.ERRORS, }); // filter values are asynchronously persisted @@ -81,18 +86,27 @@ describe('CategoryUsageDrawer', () => { usage: 50, }; SubscriptionStore.set(organization.slug, subscription); - await act(async () => { - renderComponent({ - subscription, - categoryInfo: subscription.categories.transactions, - eventTotals: { + const usageData = CustomerUsageFixture({ + totals: { + [DataCategory.TRANSACTIONS]: totals, + [DataCategory.PROFILES]: totals, + }, + stats: { + [DataCategory.TRANSACTIONS]: stats, + [DataCategory.PROFILES]: stats, + }, + eventTotals: { + [DataCategory.TRANSACTIONS]: { [DataCategory.TRANSACTIONS]: totals, [DataCategory.PROFILES]: totals, }, - totals, - stats, - periodEnd: '2021-02-01', - periodStart: '2021-01-01', + }, + }); + await act(async () => { + renderComponent({ + subscription, + selectedProduct: DataCategory.TRANSACTIONS, + usageData, }); await tick(); }); diff --git a/static/gsApp/views/subscriptionPage/usageOverview/charts.tsx b/static/gsApp/views/subscriptionPage/usageOverview/charts.tsx new file mode 100644 index 00000000000000..05315e70fc0f8f --- /dev/null +++ b/static/gsApp/views/subscriptionPage/usageOverview/charts.tsx @@ -0,0 +1,146 @@ +import {Container, Flex} from '@sentry/scraps/layout'; + +import OptionSelector from 'sentry/components/charts/optionSelector'; +import {ChartControls, InlineContainer} from 'sentry/components/charts/styles'; +import {t} from 'sentry/locale'; +import {DataCategory} from 'sentry/types/core'; +import {useLocation} from 'sentry/utils/useLocation'; +import {useNavigate} from 'sentry/utils/useNavigate'; +import {CHART_OPTIONS_DATA_TRANSFORM} from 'sentry/views/organizationStats/usageChart'; + +import {PlanTier} from 'getsentry/types'; +import {addBillingStatTotals, checkIsAddOn, isAm2Plan} from 'getsentry/utils/billing'; +import { + getCategoryInfoFromPlural, + getChunkCategoryFromDuration, + isContinuousProfiling, +} from 'getsentry/utils/dataCategory'; +import trackGetsentryAnalytics from 'getsentry/utils/trackGetsentryAnalytics'; +import { + ProductUsageChart, + selectedTransform, +} from 'getsentry/views/subscriptionPage/reservedUsageChart'; +import type {BreakdownPanelProps} from 'getsentry/views/subscriptionPage/usageOverview/types'; +import {EMPTY_STAT_TOTAL} from 'getsentry/views/subscriptionPage/usageTotals'; +import UsageTotalsTable from 'getsentry/views/subscriptionPage/usageTotalsTable'; + +function UsageCharts({ + selectedProduct, + usageData, + subscription, + organization, +}: BreakdownPanelProps) { + const navigate = useNavigate(); + const location = useLocation(); + const transform = selectedTransform(location); + if (checkIsAddOn(selectedProduct)) { + return null; + } + + const category = selectedProduct as DataCategory; + const metricHistory = subscription.categories[category]; + const categoryInfo = getCategoryInfoFromPlural(category); + + if (!metricHistory || !categoryInfo) { + return null; + } + + const {tallyType} = categoryInfo; + if (tallyType === 'seat') { + return null; + } + + const {usage: billedUsage} = metricHistory; + const stats = usageData.stats[category] ?? []; + const eventTotals = usageData.eventTotals?.[category] ?? {}; + const totals = usageData.totals[category] ?? EMPTY_STAT_TOTAL; + const usageStats = { + [category]: stats, + }; + + const adjustedTotals = isContinuousProfiling(category) + ? { + ...addBillingStatTotals(totals, [ + eventTotals[getChunkCategoryFromDuration(category)] ?? EMPTY_STAT_TOTAL, + !isAm2Plan(subscription.plan) && + selectedProduct === DataCategory.PROFILE_DURATION + ? (eventTotals[DataCategory.PROFILES] ?? EMPTY_STAT_TOTAL) + : EMPTY_STAT_TOTAL, + ]), + accepted: billedUsage, + } + : {...totals, accepted: billedUsage}; + + const showEventBreakdown = + organization.features.includes('profiling-billing') && + subscription.planTier === PlanTier.AM2 && + category === DataCategory.TRANSACTIONS; + + const renderFooter = () => { + return ( + + + { + trackGetsentryAnalytics( + 'subscription_page.usage_overview.transform_changed', + { + organization, + subscription, + transform: val, + } + ); + navigate({ + pathname: location.pathname, + query: {...location.query, transform: val}, + }); + }} + /> + + + ); + }; + + return ( + + + + + {showEventBreakdown && + Object.entries(eventTotals).map(([key, eventTotal]) => { + return ( + + ); + })} + + + ); +} + +export default UsageCharts; diff --git a/static/gsApp/views/subscriptionPage/usageOverview/index.tsx b/static/gsApp/views/subscriptionPage/usageOverview/index.tsx index a8ef6bb5c705ae..edaf1ef732b1c5 100644 --- a/static/gsApp/views/subscriptionPage/usageOverview/index.tsx +++ b/static/gsApp/views/subscriptionPage/usageOverview/index.tsx @@ -62,7 +62,6 @@ import trackGetsentryAnalytics from 'getsentry/utils/trackGetsentryAnalytics'; import {displayPriceWithCents, getBucket} from 'getsentry/views/amCheckout/utils'; import CategoryUsageDrawer from 'getsentry/views/subscriptionPage/components/categoryUsageDrawer'; import UsageOverviewActions from 'getsentry/views/subscriptionPage/usageOverview/actions'; -import {EMPTY_STAT_TOTAL} from 'getsentry/views/subscriptionPage/usageTotals'; interface UsageOverviewProps { organization: Organization; @@ -182,12 +181,8 @@ function UsageOverviewTable({subscription, organization, usageData}: UsageOvervi () => ( ), { diff --git a/static/gsApp/views/subscriptionPage/usageOverview/types.tsx b/static/gsApp/views/subscriptionPage/usageOverview/types.tsx new file mode 100644 index 00000000000000..57ff4cc91b2dc3 --- /dev/null +++ b/static/gsApp/views/subscriptionPage/usageOverview/types.tsx @@ -0,0 +1,15 @@ +import type {DataCategory} from 'sentry/types/core'; +import type {Organization} from 'sentry/types/organization'; + +import type {AddOnCategory, CustomerUsage, Subscription} from 'getsentry/types'; + +interface UsageOverviewProps { + organization: Organization; + subscription: Subscription; + usageData: CustomerUsage; +} + +export interface BreakdownPanelProps extends UsageOverviewProps { + selectedProduct: DataCategory | AddOnCategory; + isInline?: boolean; +} diff --git a/static/gsApp/views/subscriptionPage/usageTotalsTable.tsx b/static/gsApp/views/subscriptionPage/usageTotalsTable.tsx index 04d08c2ab1d7cf..2fcb7ab6c62427 100644 --- a/static/gsApp/views/subscriptionPage/usageTotalsTable.tsx +++ b/static/gsApp/views/subscriptionPage/usageTotalsTable.tsx @@ -310,12 +310,7 @@ function UsageTotalsTable({category, isEventBreakdown, totals, subscription}: Pr const hasSpikeProtection = categoryInfo?.hasSpikeProtection ?? false; return ( - + {isNewBillingUI && (