Skip to content
Original file line number Diff line number Diff line change
@@ -1,150 +1,44 @@
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<string, BillingStatTotal>;
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 (
<ChartControls>
<InlineContainer>
<OptionSelector
title={t('Type')}
selected={transform}
options={CHART_OPTIONS_DATA_TRANSFORM}
onChange={(val: string) => {
trackGetsentryAnalytics(
'subscription_page.usage_overview.transform_changed',
{
organization,
subscription,
transform: val,
}
);
navigate({
pathname: location.pathname,
query: {...location.query, transform: val},
});
}}
/>
</InlineContainer>
</ChartControls>
);
};

const showEventBreakdown =
organization.features.includes('profiling-billing') &&
subscription.planTier === PlanTier.AM2 &&
category === DataCategory.TRANSACTIONS;

return (
<Container>
<DrawerHeader>
<Flex align="center">{displayName}</Flex>
</DrawerHeader>
<DrawerBody>
<ProductUsageChart
useDisplayModeTitle={false}
usageStats={usageStats}
shouldDisplayBudgetStats={false}
reservedBudgetCategoryInfo={{}}
displayMode="usage"
<UsageCharts
selectedProduct={category}
usageData={usageData}
subscription={subscription}
category={category}
transform={transform}
usagePeriodStart={periodStart}
usagePeriodEnd={periodEnd}
footer={renderFooter()}
organization={organization}
/>
<Flex direction="column" gap="xl">
<UsageTotalsTable
category={category}
totals={adjustedTotals}
subscription={subscription}
/>
{showEventBreakdown &&
Object.entries(eventTotals).map(([key, eventTotal]) => {
return (
<UsageTotalsTable
isEventBreakdown
key={key}
category={key as DataCategory}
totals={eventTotal}
subscription={subscription}
data-test-id={`event-breakdown-${key}`}
/>
);
})}
</Flex>
</DrawerBody>
</Container>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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,
Expand All @@ -28,10 +29,10 @@ describe('CategoryUsageDrawer', () => {
organization.features.push('subscriptions-v3');
});

function renderComponent(props: any) {
function renderComponent(props: Omit<BreakdownPanelProps, 'organization'>) {
return render(
<OrganizationContext value={organization}>
<CategoryUsageDrawer {...props} />
<UsageCharts {...props} organization={organization} />
</OrganizationContext>
);
}
Expand All @@ -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
Expand Down Expand Up @@ -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();
});
Expand Down
Loading
Loading