Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
8449fb1
feat: [M3-7063] - Provide error handling when DC-specific pricing can…
cpathipa Sep 11, 2023
9549003
Code clean up
cpathipa Sep 12, 2023
d9071f4
Merge remote-tracking branch 'origin/develop' into M3-7063
cpathipa Sep 12, 2023
042de12
Adjust unit tests
cpathipa Sep 12, 2023
1e005d2
Update Currency.test.tsx
cpathipa Sep 12, 2023
3f6cd69
Update kubernetes.test.tsx
cpathipa Sep 19, 2023
0fa33a6
Merge remote-tracking branch 'origin/develop' into M3-7063
cpathipa Sep 19, 2023
99d1297
Update linodes.test.ts
cpathipa Sep 19, 2023
552eb9c
Delete linodes.test.tsx
cpathipa Sep 19, 2023
241cd20
Merge remote-tracking branch 'origin/develop' into M3-7063
cpathipa Sep 21, 2023
047360b
Code cleanup
cpathipa Sep 21, 2023
faa46ab
UX changes for UX-427
cpathipa Sep 22, 2023
d51b65e
pricing util getPrice
cpathipa Sep 22, 2023
7fca053
Update packages/manager/src/features/Kubernetes/KubernetesClusterDeta…
cpathipa Sep 28, 2023
ea6d809
Update packages/manager/src/features/Kubernetes/KubernetesClusterDeta…
cpathipa Sep 28, 2023
7d8957c
Update packages/manager/src/utilities/pricing/backups.ts
cpathipa Sep 28, 2023
3c92068
Update packages/manager/src/features/Linodes/MigrateLinode/ConfigureF…
cpathipa Sep 28, 2023
5961de5
Update packages/manager/src/features/Linodes/LinodesCreate/AddonsPane…
cpathipa Oct 3, 2023
ea425fa
Update packages/manager/src/features/components/PlansPanel/PlanSelect…
cpathipa Oct 3, 2023
ceaebc9
Update packages/manager/src/features/components/PlansPanel/PlanSelect…
cpathipa Oct 3, 2023
acb0d31
Update packages/manager/src/utilities/pricing/dynamicPricing.test.ts
cpathipa Oct 3, 2023
7dbe600
Update packages/manager/src/utilities/pricing/linodes.test.ts
cpathipa Oct 3, 2023
0d2913a
Update packages/manager/src/utilities/pricing/linodes.test.ts
cpathipa Oct 3, 2023
2cb3a0d
Update packages/manager/src/utilities/pricing/linodes.ts
cpathipa Oct 3, 2023
7784f6f
Update packages/manager/src/components/Currency/Currency.test.tsx
cpathipa Oct 3, 2023
ec8aef8
Update packages/manager/src/utilities/pricing/dynamicPricing.ts
cpathipa Oct 3, 2023
ddd78cd
Merge remote-tracking branch 'origin' into M3-7063
cpathipa Oct 3, 2023
623abb6
Update packages/manager/src/components/Currency/Currency.tsx
cpathipa Oct 3, 2023
bca3a70
Update packages/manager/src/components/DisplayPrice/DisplayPrice.tsx
cpathipa Oct 3, 2023
0181e55
Update packages/manager/src/utilities/pricing/dynamicPricing.ts
cpathipa Oct 3, 2023
799de0b
Update packages/manager/src/features/Linodes/LinodesCreate/AddonsPane…
cpathipa Oct 3, 2023
af73c35
PR - Feedback
cpathipa Oct 4, 2023
58747ee
Merge remote-tracking branch 'origin/develop' into M3-7063
cpathipa Oct 6, 2023
eb867e7
Resolve type error
cpathipa Oct 6, 2023
fbc197f
User const UNKNOWN_PRICE
cpathipa Oct 6, 2023
3f3fd64
Update packages/manager/src/components/DisplayPrice/DisplayPrice.tsx
cpathipa Oct 6, 2023
379a0eb
Use const PRICE_ERROR_NOTICE_TEXT
cpathipa Oct 6, 2023
d80ec29
Merge branch 'M3-7063' of github.com:cpathipa/manager into M3-7063
cpathipa Oct 6, 2023
14470f9
use const PRICES_RELOAD_ERROR_NOTICE_TEXT
cpathipa Oct 6, 2023
ece46f1
use const PRICES_RELOAD_ERROR_NOTICE_TEXT
cpathipa Oct 6, 2023
112d8b0
PR Feedback
cpathipa Oct 6, 2023
58f9161
Code cleanup
cpathipa Oct 6, 2023
a10a09e
Update constant name for accuracy
Oct 10, 2023
b0139a5
Clean up in Add Node Pool drawer
Oct 10, 2023
d4288d3
Clean up; match prod for Create Linode addons, summary
Oct 10, 2023
9196d1f
Fix a typo in a ternary
Oct 12, 2023
e45e5ba
Merge branch 'develop' into M3-7063
Oct 16, 2023
869718a
Address feedback
Oct 16, 2023
a6b3353
Added changeset: Dynamic price error handling for DC-specific pricing
Oct 16, 2023
3586a2a
Fix table cell error tooltip on plans table
Oct 16, 2023
bbc4602
Fix copy in resize node pools drawer causing e2e failure
Oct 16, 2023
70210ef
Clean up; fix failing backups test
Oct 16, 2023
da5bea8
PR feedback - handle NAN in calculating getTotalBackupsPrice
cpathipa Oct 17, 2023
44d2642
Reattempted, hopefully passing, not ideal backup linode types fix
Oct 17, 2023
df760f6
Merge remote-tracking branch 'origin/develop' into M3-7063
cpathipa Oct 19, 2023
b93982d
Update packages/manager/src/utilities/pricing/linodes.ts
cpathipa Oct 19, 2023
ac0b29c
Merge branch 'develop' into M3-7063
Oct 19, 2023
0338b09
Update smoke-create-nodebal.spec.ts rendering of price
Oct 19, 2023
8f08a48
Address feedback - remove unnecessary fallback
Oct 19, 2023
1102555
Address feedback - missing error icon
Oct 19, 2023
4646b77
Address feedback - disable Add button on pricing error
Oct 20, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions packages/manager/.changeset/pr-9660-added-1697473425862.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Added
---

Dynamic price error handling for DC-specific pricing ([#9660](https://github.com/linode/manager/pull/9660))
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import { ui } from 'support/ui';
import { cleanUp } from 'support/util/cleanup';
import { makeFeatureFlagData } from 'support/util/feature-flags';
import { randomLabel } from 'support/util/random';
import { dcPricingMockLinodeTypes } from 'support/constants/dc-specific-pricing';
import { dcPricingMockLinodeTypesForBackups } from 'support/constants/dc-specific-pricing';
import { chooseRegion } from 'support/util/regions';

authenticate();
Expand Down Expand Up @@ -374,13 +374,13 @@ describe('"Enable Linode Backups" banner', () => {
label: randomLabel(),
region: 'us-east',
backups: { enabled: false },
type: dcPricingMockLinodeTypes[0].id,
type: dcPricingMockLinodeTypesForBackups[0].id,
}),
linodeFactory.build({
label: randomLabel(),
region: 'us-west',
backups: { enabled: false },
type: dcPricingMockLinodeTypes[1].id,
type: dcPricingMockLinodeTypesForBackups[1].id,
}),
linodeFactory.build({
label: randomLabel(),
Expand All @@ -398,11 +398,11 @@ describe('"Enable Linode Backups" banner', () => {
];

// The expected total cost of enabling backups, as shown in backups drawer.
const expectedTotal = '$7.74/mo';
const expectedTotal = '$9.74/mo';

mockGetLinodeType(dcPricingMockLinodeTypes[0]);
mockGetLinodeType(dcPricingMockLinodeTypes[1]);
mockGetLinodeTypes(dcPricingMockLinodeTypes);
mockGetLinodeType(dcPricingMockLinodeTypesForBackups[0]);
mockGetLinodeType(dcPricingMockLinodeTypesForBackups[1]);
mockGetLinodeTypes(dcPricingMockLinodeTypesForBackups);

mockAppendFeatureFlags({
dcSpecificPricing: makeFeatureFlagData(true),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ const createNodeBalancerWithUI = (nodeBal, isDcPricingTest = false) => {
// Confirms that the price will show up when the region is selected
containsClick(selectRegionString).type(`${regionName}{enter}`);
cy.get('[data-qa-summary="true"]').within(() => {
cy.findByText(`$10.00/month`).should('be.visible');
cy.findByText(`$10/month`).should('be.visible');
});

// TODO: DC Pricing - M3-7086: Uncomment docs link assertion when docs links are added.
Expand All @@ -55,7 +55,7 @@ const createNodeBalancerWithUI = (nodeBal, isDcPricingTest = false) => {
// Confirms that the summary updates to reflect price changes if the user changes their region.
cy.get(`[value="${regionName}"]`).click().type(`${newRegion.label}{enter}`);
cy.get('[data-qa-summary="true"]').within(() => {
cy.findByText(`$14.00/month`).should('be.visible');
cy.findByText(`$14/month`).should('be.visible');
});

// Confirms that a notice is shown in the "Region" section of the NodeBalancer Create form informing the user of DC-specific pricing
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export const dcPricingMockLinodeTypes = linodeTypeFactory.buildList(3, {
backups: {
price: {
hourly: 0.004,
monthly: 2.5,
monthly: 2.0,
},
region_prices: [
{
Expand Down Expand Up @@ -75,6 +75,33 @@ export const dcPricingMockLinodeTypes = linodeTypeFactory.buildList(3, {
],
});

export const dcPricingMockLinodeTypesForBackups = linodeTypeFactory.buildList(
3,
{
addons: {
backups: {
price: {
hourly: 0.004,
monthly: 2.0,
},
region_prices: [
{
hourly: 0.0048,
id: 'us-east',
monthly: 3.57,
},
{
hourly: 0.0056,
id: 'us-west',
monthly: 4.17,
},
],
},
},
id: 'g6-nanode-1',
}
);

/**
* Subset of LKE cluster plans as shown on Cloud Manager, mapped from DC-specific pricing mock linode
* types to ensure size is consistent with ids in the types factory.
Expand Down
4 changes: 3 additions & 1 deletion packages/manager/src/MainContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,9 @@ const LoadBalancers = React.lazy(() => import('src/features/LoadBalancers'));
const NodeBalancers = React.lazy(
() => import('src/features/NodeBalancers/NodeBalancers')
);
const StackScripts = React.lazy(() => import('src/features/StackScripts/StackScripts'));
const StackScripts = React.lazy(
() => import('src/features/StackScripts/StackScripts')
);
const SupportTickets = React.lazy(
() => import('src/features/Support/SupportTickets')
);
Expand Down
8 changes: 8 additions & 0 deletions packages/manager/src/components/Currency/Currency.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as React from 'react';

import { UNKNOWN_PRICE } from 'src/utilities/pricing/constants';
import { renderWithTheme } from 'src/utilities/testHelpers';

import { Currency } from './Currency';
Expand Down Expand Up @@ -45,6 +46,13 @@ describe('Currency Component', () => {
getByText('-$5.000');
});

it('handles custom default values', () => {
const { getByText } = renderWithTheme(
<Currency quantity={UNKNOWN_PRICE} />
);
getByText(`$${UNKNOWN_PRICE}`);
});

it('groups by comma', () => {
const { getByText, rerender } = renderWithTheme(
<Currency quantity={1000} />
Expand Down
9 changes: 6 additions & 3 deletions packages/manager/src/components/Currency/Currency.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { isNumber } from 'lodash';
import * as React from 'react';

interface CurrencyFormatterProps {
dataAttrs?: Record<string, any>;
decimalPlaces?: number;
quantity: number;
quantity: '--.--' | number;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We expect currency to be either a number or an UNKNOWN_PRICE, which we define as --.--. We use the literal here, rather than typeof or string so that we avoid introducing other ways of displaying unknown prices.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like it, that's a nice fallback 👍

wrapInParentheses?: boolean;
}

Expand All @@ -16,8 +17,10 @@ export const Currency = (props: CurrencyFormatterProps) => {
style: 'currency',
});

const formattedQuantity = formatter.format(Math.abs(quantity));
const isNegative = quantity < 0;
const formattedQuantity = isNumber(quantity)
? formatter.format(Math.abs(quantity))
: `$${quantity}`;
const isNegative = isNumber(quantity) ? quantity < 0 : false;

let output;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export interface DisplayPriceProps {
decimalPlaces?: number;
fontSize?: string;
interval?: string;
price: number;
price: '--.--' | number;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We expect display price to be either a number or an UNKNOWN_PRICE, which we define as --.--. We use the literal here, rather than typeof or string so that we avoid introducing other ways of displaying unknown prices.

}

export const displayPrice = (price: number) => `$${price.toFixed(2)}`;
Expand Down
15 changes: 15 additions & 0 deletions packages/manager/src/components/TableCell/TableCell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { Theme } from '@mui/material/styles';
import * as React from 'react';
import { makeStyles } from 'tss-react/mui';

import { TooltipIcon } from 'src/components/TooltipIcon';

const useStyles = makeStyles()((theme: Theme) => ({
actionCell: {
// Prevents Safari from adding margins to the ActionMenu button
Expand Down Expand Up @@ -72,6 +74,8 @@ export interface TableCellProps extends _TableCellProps {
center?: boolean;
className?: string;
compact?: boolean;
errorCell?: boolean;
errorText?: string;
noWrap?: boolean;
/*
* parent column will either be the name of the column this
Expand All @@ -90,6 +94,8 @@ export const TableCell = (props: TableCellProps) => {
center,
className,
compact,
errorCell,
errorText,
noWrap,
parentColumn,
sortable,
Expand All @@ -116,6 +122,15 @@ export const TableCell = (props: TableCellProps) => {
>
{statusCell ? (
<div className={classes.status}>{props.children}</div>
) : errorCell && errorText ? (
<>
{props.children}
<TooltipIcon
status="error"
style={{ paddingBottom: 0, paddingTop: 0 }}
text={errorText}
/>
</>
) : (
props.children
)}
Expand Down
18 changes: 13 additions & 5 deletions packages/manager/src/features/Backups/BackupDrawer.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { styled } from '@mui/material';
import Stack from '@mui/material/Stack';
import { isNumber } from 'lodash';
import { useSnackbar } from 'notistack';
import * as React from 'react';

Expand All @@ -26,6 +27,7 @@ import { useAllLinodesQuery } from 'src/queries/linodes/linodes';
import { useAllTypes } from 'src/queries/types';
import { pluralize } from 'src/utilities/pluralize';
import { getTotalBackupsPrice } from 'src/utilities/pricing/backups';
import { UNKNOWN_PRICE } from 'src/utilities/pricing/constants';

import { AutoEnroll } from './AutoEnroll';
import { BackupLinodeRow } from './BackupLinodeRow';
Expand Down Expand Up @@ -147,6 +149,12 @@ all new Linodes will automatically be backed up.`
onClose();
};

const totalBackupsPrice = getTotalBackupsPrice({
flags,
linodes: linodesWithoutBackups,
types: types ?? [],
});

return (
<Drawer
onClose={onClose}
Expand Down Expand Up @@ -191,11 +199,11 @@ all new Linodes will automatically be backed up.`
)}
&nbsp;
<DisplayPrice
price={getTotalBackupsPrice({
flags,
linodes: linodesWithoutBackups,
types: types ?? [],
})}
price={
totalBackupsPrice && isNumber(totalBackupsPrice)
? totalBackupsPrice
: UNKNOWN_PRICE
}
interval="mo"
/>
</StyledPricingBox>
Expand Down
18 changes: 13 additions & 5 deletions packages/manager/src/features/Backups/BackupLinodeRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ import { useFlags } from 'src/hooks/useFlags';
import { useRegionsQuery } from 'src/queries/regions';
import { useTypeQuery } from 'src/queries/types';
import { getMonthlyBackupsPrice } from 'src/utilities/pricing/backups';
import {
PRICE_ERROR_TOOLTIP_TEXT,
UNKNOWN_PRICE,
} from 'src/utilities/pricing/constants';

interface Props {
error?: string;
Expand All @@ -20,7 +24,9 @@ export const BackupLinodeRow = (props: Props) => {
const { data: regions } = useRegionsQuery();
const flags = useFlags();

const backupsMonthlyPrice: PriceObject['monthly'] = getMonthlyBackupsPrice({
const backupsMonthlyPrice:
| PriceObject['monthly']
| undefined = getMonthlyBackupsPrice({
flags,
region: linode.region,
type,
Expand Down Expand Up @@ -51,10 +57,12 @@ export const BackupLinodeRow = (props: Props) => {
{flags.dcSpecificPricing && (
<TableCell parentColumn="Region">{regionLabel ?? 'Unknown'}</TableCell>
)}
<TableCell parentColumn="Price">
{backupsMonthlyPrice !== 0
? `$${backupsMonthlyPrice?.toFixed(2)}/mo`
: '$Unknown/mo'}
<TableCell
errorCell={!Boolean(backupsMonthlyPrice)}
errorText={!backupsMonthlyPrice ? PRICE_ERROR_TOOLTIP_TEXT : undefined}
parentColumn="Price"
>
{`$${backupsMonthlyPrice?.toFixed(2) ?? UNKNOWN_PRICE}/mo`}
</TableCell>
</TableRow>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -247,17 +247,10 @@ export const CreateCluster = () => {
* @returns dynamically calculated high availability price by region
*/
const getHighAvailabilityPrice = (regionId: Region['id'] | null) => {
if (!regionId) {
return undefined;
} else {
return parseFloat(
getDCSpecificPrice({
basePrice: LKE_HA_PRICE,
flags,
regionId,
})
);
}
const dcSpecificPrice = regionId
? getDCSpecificPrice({ basePrice: LKE_HA_PRICE, flags, regionId })
: undefined;
return dcSpecificPrice ? parseFloat(dcSpecificPrice) : undefined;
};

const showPricingNotice =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,17 @@ import { Divider } from 'src/components/Divider';
import { Notice } from 'src/components/Notice/Notice';
import { RenderGuard } from 'src/components/RenderGuard';
import EUAgreementCheckbox from 'src/features/Account/Agreements/EUAgreementCheckbox';
import {
getKubernetesMonthlyPrice,
getTotalClusterPrice,
} from 'src/utilities/pricing/kubernetes';
import { useFlags } from 'src/hooks/useFlags';
import { useAccountAgreements } from 'src/queries/accountAgreements';
import { useProfile } from 'src/queries/profile';
import { useSpecificTypes } from 'src/queries/types';
import { extendTypesQueryResult } from 'src/utilities/extendType';
import { isEURegion } from 'src/utilities/formatRegion';
import { LKE_CREATE_CLUSTER_CHECKOUT_MESSAGE } from 'src/utilities/pricing/constants';
import {
getKubernetesMonthlyPrice,
getTotalClusterPrice,
} from 'src/utilities/pricing/kubernetes';

import { nodeWarning } from '../kubeUtils';
import NodePoolSummary from './NodePoolSummary';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export interface Props {
nodeCount: number;
onRemove: () => void;
poolType: ExtendedType | null;
price?: number; // Can be undefined until a Region is selected.
price?: null | number; // Can be undefined until a Region is selected.
updateNodeCount: (count: number) => void;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,14 +69,16 @@ export const KubeClusterSpecs = (props: Props) => {

const displayRegion = region?.label ?? cluster.region;

const highAvailabilityPrice = cluster.control_plane.high_availability
? parseFloat(
getDCSpecificPrice({
basePrice: LKE_HA_PRICE,
flags,
regionId: region?.id,
})
)
const dcSpecificPrice = cluster.control_plane.high_availability
? getDCSpecificPrice({
basePrice: LKE_HA_PRICE,
flags,
regionId: region?.id,
})
: undefined;

const highAvailabilityPrice = dcSpecificPrice
? parseFloat(dcSpecificPrice)
: undefined;

const kubeSpecsLeft = [
Expand Down
Loading