mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-05-20 01:01:20 +00:00
Remove guest capacity from billing presentation
This commit is contained in:
parent
9217c065df
commit
efc0f371cf
7 changed files with 25 additions and 49 deletions
|
|
@ -1394,7 +1394,11 @@ when a migrated recurring v5 plan is active or in grace, the settings surface
|
|||
must render plan terms and a continuity notice that makes it clear the
|
||||
existing recurring price remains in force until cancellation, while
|
||||
self-hosted monitoring and child-resource volume stay uncapped under the
|
||||
current v6 policy.
|
||||
current v6 policy. The same plan summary must not render a separate
|
||||
`Guest Capacity`, child-resource allowance, or equivalent volume-cap row for
|
||||
uncapped/grandfathered self-hosted plans; the customer-facing continuity story
|
||||
is existing-price protection plus uncapped self-hosted monitoring and
|
||||
child-resource volume, not a new paid guest-capacity benefit.
|
||||
The self-hosted commercial presentation on that same surface is now locked to
|
||||
the no-cap monitored-system model as well. `ProLicensePanel.tsx`,
|
||||
`CommercialBillingSections.tsx`, and
|
||||
|
|
|
|||
|
|
@ -375,7 +375,7 @@ describe('ProLicensePanel', () => {
|
|||
),
|
||||
).toBeInTheDocument();
|
||||
expect(screen.queryByText('Included Monitored Systems')).not.toBeInTheDocument();
|
||||
expect(screen.getByText('Guest Capacity')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Guest Capacity')).not.toBeInTheDocument();
|
||||
expect(screen.getByText('Core Monitoring')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Monitored Systems')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Capacity Status')).not.toBeInTheDocument();
|
||||
|
|
@ -423,17 +423,17 @@ describe('ProLicensePanel', () => {
|
|||
expect(screen.getByText('Grandfathered price')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getAllByText(
|
||||
/keeps its existing recurring price and uncapped monitored-system and guest capacity/i,
|
||||
/keeps its existing recurring price until/i,
|
||||
).length,
|
||||
).toBeGreaterThan(0);
|
||||
expect(
|
||||
screen.queryByText(/keeps its existing recurring price and uncapped guest capacity/i),
|
||||
).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(
|
||||
/keeps its existing recurring price and uncapped monitored-system and guest capacity until you cancel/i,
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
screen.getAllByText(
|
||||
/self-hosted monitoring and child-resource volume remain uncapped under the current v6 policy/i,
|
||||
).length,
|
||||
).toBeGreaterThan(0);
|
||||
expect(
|
||||
within(screen.getByText('Core Monitoring').parentElement as HTMLElement).getByText(
|
||||
'Unlimited',
|
||||
|
|
@ -441,7 +441,7 @@ describe('ProLicensePanel', () => {
|
|||
).toBeInTheDocument();
|
||||
expect(screen.queryByText('Capacity Status')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Included Monitored Systems')).not.toBeInTheDocument();
|
||||
expect(screen.getByText('Guest Capacity')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Guest Capacity')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Monitored-system policy')).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole('tab', { name: 'Usage' })).not.toBeInTheDocument();
|
||||
expect(screen.getAllByText('Unlimited').length).toBeGreaterThan(0);
|
||||
|
|
|
|||
|
|
@ -162,9 +162,6 @@ export function useProLicensePanelState() {
|
|||
const monitoredSystemCapacity = createMemo(() =>
|
||||
uncappedGrandfatheredPlan() ? undefined : entitlements()?.monitored_system_capacity,
|
||||
);
|
||||
const guestLimitStatus = createMemo(() =>
|
||||
uncappedGrandfatheredPlan() ? undefined : limitStatus('max_guests'),
|
||||
);
|
||||
const displayableMonitoredSystemContinuity = createMemo(() =>
|
||||
getDisplayableMonitoredSystemContinuity({
|
||||
continuity: monitoredSystemContinuity(),
|
||||
|
|
@ -507,17 +504,6 @@ export function useProLicensePanelState() {
|
|||
),
|
||||
);
|
||||
|
||||
const showGuestCapacity = createMemo(() => {
|
||||
if (currentRetailPlanDefinition()) {
|
||||
return false;
|
||||
}
|
||||
if (uncappedGrandfatheredPlan()) {
|
||||
return true;
|
||||
}
|
||||
const guestLimit = guestLimitStatus()?.limit;
|
||||
return typeof guestLimit === 'number' && guestLimit > 0;
|
||||
});
|
||||
|
||||
const commercialPlanModel = createMemo(() =>
|
||||
buildSelfHostedCommercialPlanModel({
|
||||
licensedEmail: entitlements()?.licensed_email,
|
||||
|
|
@ -533,12 +519,7 @@ export function useProLicensePanelState() {
|
|||
monitoredSystemLimitStatus()!.limit > 0
|
||||
? monitoredSystemLimitStatus()!.limit
|
||||
: 'Unlimited',
|
||||
guestCapacity:
|
||||
typeof guestLimitStatus()?.limit === 'number' && guestLimitStatus()!.limit > 0
|
||||
? guestLimitStatus()!.limit
|
||||
: 'Unlimited',
|
||||
retailPlanDefinition: currentRetailPlanDefinition(),
|
||||
showGuestCapacity: showGuestCapacity(),
|
||||
monitoredSystemContinuity: displayableMonitoredSystemContinuity() ?? null,
|
||||
continuityCapturedAt: continuityCapturedAt(),
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ const createBaseInput = () => ({
|
|||
monitoredSystemsSummary: 'Unlimited',
|
||||
capacityStatusSummary: 'Unlimited',
|
||||
maxMonitoredSystems: 'Unlimited' as const,
|
||||
guestCapacity: 'Unlimited' as const,
|
||||
});
|
||||
|
||||
describe('commercialBillingModel', () => {
|
||||
|
|
@ -21,7 +20,6 @@ describe('commercialBillingModel', () => {
|
|||
const model = buildSelfHostedCommercialPlanModel({
|
||||
...createBaseInput(),
|
||||
retailPlanDefinition: SELF_HOSTED_PLAN_BY_TIER.pro,
|
||||
showGuestCapacity: false,
|
||||
});
|
||||
|
||||
expect(model.summary).toEqual([
|
||||
|
|
@ -38,20 +36,19 @@ describe('commercialBillingModel', () => {
|
|||
]);
|
||||
});
|
||||
|
||||
it('keeps guest capacity visible for uncapped grandfathered continuity states', () => {
|
||||
it('keeps uncapped grandfathered continuity focused on core monitoring', () => {
|
||||
const model = buildSelfHostedCommercialPlanModel({
|
||||
...createBaseInput(),
|
||||
planTerms: 'V5 Pro Monthly (Grandfathered)',
|
||||
retailPlanDefinition: null,
|
||||
showGuestCapacity: true,
|
||||
});
|
||||
|
||||
expect(model.summary).toEqual([
|
||||
{ label: 'Core Monitoring', value: 'Unlimited' },
|
||||
{ label: 'Guest Capacity', value: 'Unlimited' },
|
||||
{ label: 'Plan Status', value: 'Active' },
|
||||
]);
|
||||
expect(model.details.map((item) => item.label)).not.toContain('Included Monitored Systems');
|
||||
expect(model.summary.map((item) => item.label)).not.toContain('Guest Capacity');
|
||||
});
|
||||
|
||||
it('keeps bounded monitored-system details on legacy fallback paths', () => {
|
||||
|
|
@ -60,9 +57,7 @@ describe('commercialBillingModel', () => {
|
|||
monitoredSystemsSummary: '7 monitored systems',
|
||||
capacityStatusSummary: '3 remaining',
|
||||
maxMonitoredSystems: 10,
|
||||
guestCapacity: 50,
|
||||
retailPlanDefinition: null,
|
||||
showGuestCapacity: true,
|
||||
});
|
||||
|
||||
expect(model.summary).toEqual([
|
||||
|
|
|
|||
|
|
@ -193,8 +193,14 @@ describe('licensePresentation', () => {
|
|||
).toMatchObject({
|
||||
title: 'Grandfathered v5 pricing',
|
||||
tone: expect.stringContaining('green'),
|
||||
body: expect.stringContaining('uncapped monitored-system and guest capacity'),
|
||||
body: expect.stringContaining('keeps its existing recurring price until you cancel'),
|
||||
});
|
||||
expect(
|
||||
getGrandfatheredPriceContinuityNotice('v5_pro_monthly_grandfathered', 'active')?.body,
|
||||
).toContain('Self-hosted monitoring and child-resource volume remain uncapped');
|
||||
expect(
|
||||
getGrandfatheredPriceContinuityNotice('v5_pro_monthly_grandfathered', 'active')?.body,
|
||||
).not.toContain('guest capacity');
|
||||
expect(
|
||||
getGrandfatheredPriceContinuityNotice('v5_pro_annual_grandfathered', 'grace'),
|
||||
).toMatchObject({
|
||||
|
|
@ -343,7 +349,7 @@ describe('licensePresentation', () => {
|
|||
],
|
||||
supplementalBadges: ['Grandfathered price'],
|
||||
supplementalSummary:
|
||||
'This migrated v5 subscription keeps its existing recurring price and uncapped monitored-system and guest capacity until cancellation.',
|
||||
'This migrated v5 subscription keeps its existing recurring price until cancellation. Self-hosted monitoring and child-resource volume remain uncapped under the current v6 policy.',
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -33,12 +33,10 @@ export interface SelfHostedCommercialModelInput {
|
|||
monitoredSystemsSummary: string | number;
|
||||
capacityStatusSummary: string | number;
|
||||
maxMonitoredSystems: string | number;
|
||||
guestCapacity: string | number;
|
||||
retailPlanDefinition?: Pick<
|
||||
SelfHostedPlanDefinition,
|
||||
'billingExtrasSummary' | 'metricHistoryDays'
|
||||
> | null;
|
||||
showGuestCapacity?: boolean;
|
||||
monitoredSystemContinuity?: MonitoredSystemContinuityStatus | null;
|
||||
continuityCapturedAt?: string;
|
||||
}
|
||||
|
|
@ -123,14 +121,6 @@ export const buildSelfHostedCommercialPlanModel = (
|
|||
label: 'Core Monitoring',
|
||||
value: 'Unlimited',
|
||||
},
|
||||
...(input.showGuestCapacity
|
||||
? [
|
||||
{
|
||||
label: 'Guest Capacity',
|
||||
value: input.guestCapacity,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
label: 'Plan Status',
|
||||
value: input.statusLabel,
|
||||
|
|
|
|||
|
|
@ -251,7 +251,7 @@ export const getGrandfatheredPriceContinuityNotice = (
|
|||
return {
|
||||
tone: 'border-green-200 dark:border-green-900 bg-green-50 dark:bg-green-900 text-green-900 dark:text-green-100',
|
||||
title: 'Grandfathered v5 pricing',
|
||||
body: 'This migrated v5 Pro subscription keeps its existing recurring price and uncapped monitored-system and guest capacity until you cancel. If you cancel and return later, current v6 pricing applies; public self-hosted monitoring remains unlimited.',
|
||||
body: 'This migrated v5 Pro subscription keeps its existing recurring price until you cancel. Self-hosted monitoring and child-resource volume remain uncapped under the current v6 policy. If you cancel and return later, current v6 pricing applies for paid features.',
|
||||
};
|
||||
};
|
||||
|
||||
|
|
@ -481,12 +481,12 @@ export const getSelfHostedCurrentPlanPresentation = ({
|
|||
) {
|
||||
supplementalBadges.push('Grandfathered price');
|
||||
supplementalDetails.push(
|
||||
'This migrated v5 subscription keeps its existing recurring price and uncapped monitored-system and guest capacity until cancellation.',
|
||||
'This migrated v5 subscription keeps its existing recurring price until cancellation. Self-hosted monitoring and child-resource volume remain uncapped under the current v6 policy.',
|
||||
);
|
||||
} else if (hasUncappedContinuity && current.is_lifetime) {
|
||||
supplementalBadges.push('Grandfathered lifetime');
|
||||
supplementalDetails.push(
|
||||
'This migrated lifetime install keeps uncapped monitored-system and guest capacity continuity.',
|
||||
'This migrated lifetime install remains valid permanently, with uncapped self-hosted monitoring and child-resource volume.',
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue