mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-23 12:15:42 +00:00
Align in-app Plans & Billing copy with the new public pricing vocabulary
The public pricing surface (pulserelay.pro and the license-server pricing model, pulse-pro 3d9e365/2da1290) now sells Pro with the real Patrol mode names and plain-English job framing. The in-app plan cards still used the old taxonomy (Choose Patrol mode, Patrol investigates issues, Patrol handles safe fixes) and 'admin controls' phrasing. Pro plan-selling copy now matches the public wording: mode names spelled out (Ask first, Safe auto-fix, Autopilot), investigation explains the root cause, safe fixes are verified, and RBAC/audit/reporting/agent profiles are 'team controls'. Functional CTAs (the Choose Patrol mode link) and canonical mode labels are unchanged. Test pins updated. The Go-side feature catalog names (Patrol Investigates Issues / Patrol Handles Safe Fixes) still carry the old phrasing and are a separate follow-up since they regenerate from pkg/licensing.
This commit is contained in:
@@ -310,12 +310,19 @@ describe('ProLicensePanel', () => {
|
||||
expect(screen.getByText('Remote web access via Relay')).toBeInTheDocument();
|
||||
expect(screen.getByText('Pulse Mobile pairing')).toBeInTheDocument();
|
||||
expect(screen.getByText('Push notifications')).toBeInTheDocument();
|
||||
expect(screen.getByText('Choose Patrol mode')).toBeInTheDocument();
|
||||
expect(screen.getByText('Patrol investigates issues')).toBeInTheDocument();
|
||||
expect(screen.getByText('Patrol handles safe fixes')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText('Patrol modes: Ask first, Safe auto-fix, or Autopilot'),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText('Patrol investigates issues and explains the root cause'),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText('Patrol applies safe fixes and verifies the result'),
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByText('90-day metric history')).toBeInTheDocument();
|
||||
expect(screen.getByText('RBAC and audit logging')).toBeInTheDocument();
|
||||
expect(screen.getByText('Agent profiles · PDF/CSV reports')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText('Team controls: RBAC, audit logging, reporting, and agent profiles'),
|
||||
).toBeInTheDocument();
|
||||
|
||||
expect(screen.queryByText(/unlimited/i)).not.toBeInTheDocument();
|
||||
expect(screen.queryByText(/trial/i)).not.toBeInTheDocument();
|
||||
@@ -825,7 +832,7 @@ describe('ProLicensePanel', () => {
|
||||
expect(screen.getByText('Current plan: Pulse Pro')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(
|
||||
'Pulse Pro is active on this instance. It includes Patrol modes, 90-day metric history, RBAC, audit logs, reports, and agent profiles.',
|
||||
'Pulse Pro is active on this instance. It includes Patrol modes (Ask first, Safe auto-fix, Autopilot), 90-day metric history, RBAC, audit logging, reporting, and agent profiles.',
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByText('Primary capabilities')).toBeInTheDocument();
|
||||
@@ -851,7 +858,7 @@ describe('ProLicensePanel', () => {
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByText('Patrol investigation and remediation')).toBeInTheDocument();
|
||||
expect(screen.getByText('Team and admin controls')).toBeInTheDocument();
|
||||
expect(screen.getByText('Team controls')).toBeInTheDocument();
|
||||
expect(screen.getAllByText('Active').length).toBeGreaterThanOrEqual(4);
|
||||
expect(
|
||||
screen.queryByText(new RegExp(['optional', 'activation'].join(' '), 'i')),
|
||||
@@ -863,7 +870,7 @@ describe('ProLicensePanel', () => {
|
||||
expect(screen.queryByText('Needs attention')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Available plans')).not.toBeInTheDocument();
|
||||
expect(screen.getByText('90 days')).toBeInTheDocument();
|
||||
expect(screen.getByText('Patrol modes, history, and admin controls')).toBeInTheDocument();
|
||||
expect(screen.getByText('Patrol modes, history, and team controls')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Guest Capacity')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Included Monitored Systems')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Monitored-system policy')).not.toBeInTheDocument();
|
||||
|
||||
@@ -22,7 +22,7 @@ describe('commercialBillingModel', () => {
|
||||
expect(model.summary).toEqual([
|
||||
{ label: 'Core Monitoring', value: 'Included' },
|
||||
{ label: 'Metric History', value: '90 days' },
|
||||
{ label: 'Included Extras', value: 'Patrol modes, history, and admin controls' },
|
||||
{ label: 'Included Extras', value: 'Patrol modes, history, and team controls' },
|
||||
]);
|
||||
expect(model.details.map((item) => item.label)).toEqual([
|
||||
'Tier',
|
||||
|
||||
@@ -339,7 +339,7 @@ describe('licensePresentation', () => {
|
||||
}),
|
||||
).toMatchObject({
|
||||
title: 'Current plan: Pulse Pro',
|
||||
body: 'Pulse Pro is active on this instance. It includes Patrol modes, 90-day metric history, RBAC, audit logs, reports, and agent profiles.',
|
||||
body: 'Pulse Pro is active on this instance. It includes Patrol modes (Ask first, Safe auto-fix, Autopilot), 90-day metric history, RBAC, audit logging, reporting, and agent profiles.',
|
||||
supplementalBadges: [],
|
||||
supplementalSummary: '',
|
||||
});
|
||||
@@ -362,7 +362,7 @@ describe('licensePresentation', () => {
|
||||
}),
|
||||
).toEqual({
|
||||
title: 'Current plan: Pulse Pro',
|
||||
body: 'Pulse Pro is active on this instance. It includes Patrol modes, 90-day metric history, RBAC, audit logs, reports, and agent profiles.',
|
||||
body: 'Pulse Pro is active on this instance. It includes Patrol modes (Ask first, Safe auto-fix, Autopilot), 90-day metric history, RBAC, audit logging, reporting, and agent profiles.',
|
||||
unlockedFeaturesLabel: 'Primary capabilities',
|
||||
unlockedFeatures: [
|
||||
'Patrol Investigates Issues',
|
||||
@@ -633,7 +633,7 @@ describe('licensePresentation', () => {
|
||||
}),
|
||||
).toMatchObject({
|
||||
title: 'Current plan: Legacy Pulse Pro+',
|
||||
body: 'Legacy Pulse Pro+ is active on this instance. It includes Patrol modes, 90-day metric history, RBAC, audit logs, reports, and agent profiles.',
|
||||
body: 'Legacy Pulse Pro+ is active on this instance. It includes Patrol modes (Ask first, Safe auto-fix, Autopilot), 90-day metric history, RBAC, audit logging, reporting, and agent profiles.',
|
||||
unlockedFeaturesLabel: 'Primary capabilities',
|
||||
unlockedFeatures: [
|
||||
'Patrol Investigates Issues',
|
||||
@@ -657,7 +657,7 @@ describe('licensePresentation', () => {
|
||||
}),
|
||||
).toEqual({
|
||||
title: 'Current plan: Pulse Pro',
|
||||
body: 'Pulse Pro is active on this instance. It includes Patrol modes, 90-day metric history, RBAC, audit logs, reports, and agent profiles.',
|
||||
body: 'Pulse Pro is active on this instance. It includes Patrol modes (Ask first, Safe auto-fix, Autopilot), 90-day metric history, RBAC, audit logging, reporting, and agent profiles.',
|
||||
unlockedFeaturesLabel: 'Primary capabilities',
|
||||
unlockedFeatures: [
|
||||
'Patrol Investigates Issues',
|
||||
@@ -903,15 +903,14 @@ describe('licensePresentation', () => {
|
||||
},
|
||||
{
|
||||
title: 'Pulse Pro plan',
|
||||
body: 'Patrol modes, 90-day metric history, RBAC, audit logs, reports, and agent profiles.',
|
||||
body: 'Patrol investigates issues, applies safe fixes, and verifies the result, plus 90-day metric history and team controls.',
|
||||
highlights: [
|
||||
'Everything in Relay',
|
||||
'Choose Patrol mode',
|
||||
'Patrol investigates issues',
|
||||
'Patrol handles safe fixes',
|
||||
'Patrol modes: Ask first, Safe auto-fix, or Autopilot',
|
||||
'Patrol investigates issues and explains the root cause',
|
||||
'Patrol applies safe fixes and verifies the result',
|
||||
'90-day metric history',
|
||||
'RBAC and audit logging',
|
||||
'Agent profiles · PDF/CSV reports',
|
||||
'Team controls: RBAC, audit logging, reporting, and agent profiles',
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -931,15 +930,14 @@ describe('licensePresentation', () => {
|
||||
cards: [
|
||||
{
|
||||
title: 'Pulse Pro plan',
|
||||
body: 'Patrol modes, 90-day metric history, RBAC, audit logs, reports, and agent profiles.',
|
||||
body: 'Patrol investigates issues, applies safe fixes, and verifies the result, plus 90-day metric history and team controls.',
|
||||
highlights: [
|
||||
'Everything in Relay',
|
||||
'Choose Patrol mode',
|
||||
'Patrol investigates issues',
|
||||
'Patrol handles safe fixes',
|
||||
'Patrol modes: Ask first, Safe auto-fix, or Autopilot',
|
||||
'Patrol investigates issues and explains the root cause',
|
||||
'Patrol applies safe fixes and verifies the result',
|
||||
'90-day metric history',
|
||||
'RBAC and audit logging',
|
||||
'Agent profiles · PDF/CSV reports',
|
||||
'Team controls: RBAC, audit logging, reporting, and agent profiles',
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -1018,7 +1016,7 @@ describe('licensePresentation', () => {
|
||||
{ label: 'Remote access, pairing, and push', statusLabel: 'Active' },
|
||||
{ label: '90-day metric history', statusLabel: 'Active' },
|
||||
{ label: 'Patrol investigation and remediation', statusLabel: 'Active' },
|
||||
{ label: 'Team and admin controls', statusLabel: 'Active' },
|
||||
{ label: 'Team controls', statusLabel: 'Active' },
|
||||
],
|
||||
});
|
||||
|
||||
@@ -1036,7 +1034,7 @@ describe('licensePresentation', () => {
|
||||
['Remote access, pairing, and push', 'Partial'],
|
||||
['90-day metric history', 'Partial'],
|
||||
['Patrol investigation and remediation', 'Partial'],
|
||||
['Team and admin controls', 'Needs attention'],
|
||||
['Team controls', 'Needs attention'],
|
||||
]);
|
||||
expect(partialProStatus?.items).toContainEqual({
|
||||
label: 'Patrol investigation and remediation',
|
||||
@@ -1046,11 +1044,11 @@ describe('licensePresentation', () => {
|
||||
'Some Patrol capability is available, but investigation or remediation may not be fully enabled yet.',
|
||||
});
|
||||
expect(partialProStatus?.items).toContainEqual({
|
||||
label: 'Team and admin controls',
|
||||
label: 'Team controls',
|
||||
state: 'missing',
|
||||
statusLabel: 'Needs attention',
|
||||
detail:
|
||||
'Team/admin controls are not available yet. Refresh the plan or open recovery before relying on this Pro install.',
|
||||
'Team controls are not available yet. Refresh the plan or open recovery before relying on this Pro install.',
|
||||
});
|
||||
expect(partialProStatus?.items.map((item) => item.detail).join(' ')).not.toContain(
|
||||
'admin tools',
|
||||
@@ -1093,7 +1091,7 @@ describe('licensePresentation', () => {
|
||||
['Remote access, pairing, and push', 'Active'],
|
||||
['90-day metric history', 'Active'],
|
||||
['Patrol investigation and remediation', 'Active'],
|
||||
['Team and admin controls', 'Active'],
|
||||
['Team controls', 'Active'],
|
||||
];
|
||||
const expectOnlyCapabilityItems = (
|
||||
status?: ReturnType<typeof getSelfHostedPlanStatusPresentation>,
|
||||
|
||||
@@ -126,13 +126,13 @@ describe('selfHostedPlans', () => {
|
||||
/yourlab\.pulserelay\.pro|custom\s+(?:url|subdomain|domain)/i,
|
||||
);
|
||||
expect(SELF_HOSTED_PLAN_BY_TIER.pro.billingExtrasSummary).toBe(
|
||||
'Patrol modes, history, and admin controls',
|
||||
'Patrol modes, history, and team controls',
|
||||
);
|
||||
expect(SELF_HOSTED_PLAN_BY_TIER.pro.entitlementSummary).toContain(
|
||||
'Patrol modes, 90-day metric history, RBAC, audit logs, reports, and agent profiles.',
|
||||
'Patrol modes (Ask first, Safe auto-fix, Autopilot), 90-day metric history, RBAC, audit logging, reporting, and agent profiles.',
|
||||
);
|
||||
expect(SELF_HOSTED_PLAN_BY_TIER.pro.comparisonSummary).toBe(
|
||||
'Patrol modes, 90-day metric history, RBAC, audit logs, reports, and agent profiles.',
|
||||
'Patrol investigates issues, applies safe fixes, and verifies the result, plus 90-day metric history and team controls.',
|
||||
);
|
||||
expect(SELF_HOSTED_PLAN_BY_TIER.pro.comparisonSummary).not.toContain('how much control');
|
||||
expect(SELF_HOSTED_PLAN_BY_TIER.pro.entitlementHighlights).toEqual([
|
||||
@@ -172,7 +172,7 @@ describe('selfHostedPlans', () => {
|
||||
getSelfHostedPlanEntitlementSummary('pro'),
|
||||
);
|
||||
expect(getSelfHostedPlanEntitlementSummary('pro', 'Legacy Pulse Pro+')).toBe(
|
||||
'Legacy Pulse Pro+ is active on this instance. It includes Patrol modes, 90-day metric history, RBAC, audit logs, reports, and agent profiles.',
|
||||
'Legacy Pulse Pro+ is active on this instance. It includes Patrol modes (Ask first, Safe auto-fix, Autopilot), 90-day metric history, RBAC, audit logging, reporting, and agent profiles.',
|
||||
);
|
||||
|
||||
expect(SELF_HOSTED_PLAN_BY_TIER.relay.entitlementHighlights).toEqual(
|
||||
@@ -216,7 +216,7 @@ describe('selfHostedPlans', () => {
|
||||
historyDays: 14,
|
||||
extras: 'Remote web access, pairing, and push',
|
||||
},
|
||||
{ tier: 'pro', historyDays: 90, extras: 'Patrol modes, history, and admin controls' },
|
||||
{ tier: 'pro', historyDays: 90, extras: 'Patrol modes, history, and team controls' },
|
||||
]);
|
||||
expect(SELF_HOSTED_FEATURE_ROWS.find((row) => row.key === 'history')).toEqual({
|
||||
key: 'history',
|
||||
|
||||
@@ -924,14 +924,14 @@ export const getSelfHostedPlanStatusPresentation = (
|
||||
}),
|
||||
buildCapabilityStatusItem({
|
||||
capabilities,
|
||||
label: 'Team and admin controls',
|
||||
label: 'Team controls',
|
||||
requiredCapabilities: ['rbac', 'audit_logging', 'advanced_reporting', 'agent_profiles'],
|
||||
activeDetail:
|
||||
'RBAC, audit logging, reporting, and agent profiles are available for governed operations.',
|
||||
partialDetail:
|
||||
'Some team/admin capabilities are available. Refresh the plan or open recovery if any admin controls stay unavailable.',
|
||||
'Some team controls are available. Refresh the plan or open recovery if any of them stay unavailable.',
|
||||
missingDetail:
|
||||
'Team/admin controls are not available yet. Refresh the plan or open recovery before relying on this Pro install.',
|
||||
'Team controls are not available yet. Refresh the plan or open recovery before relying on this Pro install.',
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ export function getSelfHostedPlanEntitlementSummary(
|
||||
case 'relay':
|
||||
return `${planLabel} is active on this instance. It includes remote web access, Pulse Mobile pairing, push notifications, and 14-day metric history.`;
|
||||
case 'pro':
|
||||
return `${planLabel} is active on this instance. It includes Patrol modes, 90-day metric history, RBAC, audit logs, reports, and agent profiles.`;
|
||||
return `${planLabel} is active on this instance. It includes Patrol modes (Ask first, Safe auto-fix, Autopilot), 90-day metric history, RBAC, audit logging, reporting, and agent profiles.`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,20 +150,19 @@ export const SELF_HOSTED_PLAN_DEFINITIONS: readonly SelfHostedPlanDefinition[] =
|
||||
price: '$79/year',
|
||||
subline: 'or $8.99/month',
|
||||
metricHistoryDays: 90,
|
||||
billingExtrasSummary: 'Patrol modes, history, and admin controls',
|
||||
billingExtrasSummary: 'Patrol modes, history, and team controls',
|
||||
entitlementSummary: getSelfHostedPlanEntitlementSummary('pro'),
|
||||
entitlementHighlights: getTierEntitlementHighlights('pro', 90),
|
||||
includedExtras: getTierIncludedExtras('pro'),
|
||||
comparisonSummary:
|
||||
'Patrol modes, 90-day metric history, RBAC, audit logs, reports, and agent profiles.',
|
||||
'Patrol investigates issues, applies safe fixes, and verifies the result, plus 90-day metric history and team controls.',
|
||||
highlights: [
|
||||
'Everything in Relay',
|
||||
'Choose Patrol mode',
|
||||
'Patrol investigates issues',
|
||||
'Patrol handles safe fixes',
|
||||
'Patrol modes: Ask first, Safe auto-fix, or Autopilot',
|
||||
'Patrol investigates issues and explains the root cause',
|
||||
'Patrol applies safe fixes and verifies the result',
|
||||
'90-day metric history',
|
||||
'RBAC and audit logging',
|
||||
'Agent profiles · PDF/CSV reports',
|
||||
'Team controls: RBAC, audit logging, reporting, and agent profiles',
|
||||
],
|
||||
},
|
||||
] as const;
|
||||
|
||||
Reference in New Issue
Block a user