mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-23 20:25:56 +00:00
Move Pro license notices into shared presentation
This commit is contained in:
@@ -516,6 +516,10 @@ The same rule applies to self-hosted pricing framing: page title/description,
|
||||
badge copy, primary CTA labels, and adjacent upsell-link copy on
|
||||
`PricingV6.tsx` must come from the shared self-hosted plan-definition owner
|
||||
rather than page-local strings.
|
||||
The shared license presentation owner also holds self-hosted Pro settings
|
||||
upsell and trial-ended notice copy for `ProLicensePlanSection.tsx`; that
|
||||
surface must consume canonical helper notices instead of carrying inline
|
||||
upgrade copy or local status-tone branches.
|
||||
That same counted-unit boundary also owns the disclosure rule for retail copy:
|
||||
default billing and pricing surfaces should use concise monitored-system copy,
|
||||
while the full counted-unit definition appears only behind explicit disclosure
|
||||
|
||||
@@ -2,8 +2,10 @@ import { Component, For, Show } from 'solid-js';
|
||||
import RefreshCw from 'lucide-solid/icons/refresh-cw';
|
||||
import { getUpgradeActionUrlOrFallback, licenseLoadError } from '@/stores/license';
|
||||
import {
|
||||
getInactiveProUpsellNotice,
|
||||
getLicenseStatusLoadingState,
|
||||
getNoActiveProLicenseState,
|
||||
getTrialEndedProLicenseNotice,
|
||||
} from '@/utils/licensePresentation';
|
||||
import { CommercialStatGrid } from './CommercialBillingSections';
|
||||
|
||||
@@ -44,6 +46,9 @@ const formatDate = (value?: string | null) => {
|
||||
return date.toLocaleDateString();
|
||||
};
|
||||
|
||||
const trialEndedNotice = getTrialEndedProLicenseNotice();
|
||||
const inactiveProUpsellNotice = getInactiveProUpsellNotice();
|
||||
|
||||
export const ProLicensePlanSection: Component<ProLicensePlanSectionProps> = (props) => (
|
||||
<>
|
||||
<Show when={props.trialActivationNotice}>
|
||||
@@ -71,16 +76,16 @@ export const ProLicensePlanSection: Component<ProLicensePlanSectionProps> = (pro
|
||||
)}
|
||||
</Show>
|
||||
<Show when={props.trialEnded && !licenseLoadError()}>
|
||||
<div class="mb-4 rounded-md border border-red-200 dark:border-red-900 bg-red-50 dark:bg-red-900 p-3 text-sm text-red-900 dark:text-red-100">
|
||||
<p class="font-medium">Your Pro trial has ended</p>
|
||||
<p class="text-xs text-red-800 dark:text-red-200 mt-1">Upgrade to keep Pro features.</p>
|
||||
<div class={`mb-4 rounded-md border p-3 text-sm ${trialEndedNotice.tone}`}>
|
||||
<p class="font-medium">{trialEndedNotice.title}</p>
|
||||
<p class="text-xs mt-1 opacity-90">{trialEndedNotice.body}</p>
|
||||
<a
|
||||
class="inline-flex items-center gap-1 mt-2 text-xs font-medium text-red-900 dark:text-red-100 hover:underline"
|
||||
class="inline-flex items-center gap-1 mt-2 text-xs font-medium hover:underline"
|
||||
href={getUpgradeActionUrlOrFallback('trial_expired')}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
View Pro plans
|
||||
{trialEndedNotice.actionLabel}
|
||||
</a>
|
||||
</div>
|
||||
</Show>
|
||||
@@ -149,18 +154,16 @@ export const ProLicensePlanSection: Component<ProLicensePlanSectionProps> = (pro
|
||||
</Show>
|
||||
|
||||
<Show when={!props.hasPaidFeatures && !props.trialEnded}>
|
||||
<div class="rounded-md border border-amber-200 dark:border-amber-800 bg-amber-50 dark:bg-amber-900 p-3 text-sm text-amber-800 dark:text-amber-200">
|
||||
<p class="font-medium">Upgrade to Pro</p>
|
||||
<p class="text-xs text-amber-700 dark:text-amber-300 mt-1">
|
||||
Unlock Pulse Patrol, alert analysis, auto-fix, and more.
|
||||
</p>
|
||||
<div class={`rounded-md border p-3 text-sm ${inactiveProUpsellNotice.tone}`}>
|
||||
<p class="font-medium">{inactiveProUpsellNotice.title}</p>
|
||||
<p class="text-xs mt-1 opacity-90">{inactiveProUpsellNotice.body}</p>
|
||||
<a
|
||||
class="inline-flex items-center gap-1 mt-2 text-xs font-medium text-amber-800 dark:text-amber-200 hover:underline"
|
||||
class="inline-flex items-center gap-1 mt-2 text-xs font-medium hover:underline"
|
||||
href={getUpgradeActionUrlOrFallback('ai_autofix')}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
View Pro plans
|
||||
{inactiveProUpsellNotice.actionLabel}
|
||||
</a>
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
@@ -466,5 +466,9 @@ describe('ProLicensePanel', () => {
|
||||
expect(proLicensePanelStateSource).not.toContain('startProTrial()');
|
||||
expect(proLicensePlanSectionSource).toContain('getLicenseStatusLoadingState');
|
||||
expect(proLicensePlanSectionSource).toContain('getNoActiveProLicenseState');
|
||||
expect(proLicensePlanSectionSource).toContain('getTrialEndedProLicenseNotice');
|
||||
expect(proLicensePlanSectionSource).toContain('getInactiveProUpsellNotice');
|
||||
expect(proLicensePlanSectionSource).not.toContain('Your Pro trial has ended');
|
||||
expect(proLicensePlanSectionSource).not.toContain('Unlock Pulse Patrol, alert analysis, auto-fix, and more.');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,6 +14,8 @@ import {
|
||||
getLicenseTierLabel,
|
||||
getNoActiveProLicenseState,
|
||||
getOrganizationBillingLicenseStatusLabel,
|
||||
getInactiveProUpsellNotice,
|
||||
getTrialEndedProLicenseNotice,
|
||||
getTrialActivationNotice,
|
||||
isDisplayableLicenseFeature,
|
||||
} from '@/utils/licensePresentation';
|
||||
@@ -33,6 +35,18 @@ describe('licensePresentation', () => {
|
||||
expect(getNoActiveProLicenseState()).toEqual({
|
||||
text: 'No Pro license is active.',
|
||||
});
|
||||
expect(getTrialEndedProLicenseNotice()).toEqual({
|
||||
tone: 'border-red-200 dark:border-red-900 bg-red-50 dark:bg-red-900 text-red-900 dark:text-red-100',
|
||||
title: 'Your Pro trial has ended',
|
||||
body: 'Upgrade to keep Pro features.',
|
||||
actionLabel: 'View Pro plans',
|
||||
});
|
||||
expect(getInactiveProUpsellNotice()).toEqual({
|
||||
tone: 'border-amber-200 dark:border-amber-800 bg-amber-50 dark:bg-amber-900 text-amber-800 dark:text-amber-200',
|
||||
title: 'Upgrade to Pro',
|
||||
body: 'Unlock Pulse Patrol, alert analysis, auto-fix, and more.',
|
||||
actionLabel: 'View Pro plans',
|
||||
});
|
||||
});
|
||||
|
||||
it('returns canonical feature labels', () => {
|
||||
|
||||
@@ -65,6 +65,10 @@ export interface LicenseInlineNotice {
|
||||
body: string;
|
||||
}
|
||||
|
||||
export interface LicenseActionNotice extends LicenseInlineNotice {
|
||||
actionLabel: string;
|
||||
}
|
||||
|
||||
export interface BillingAdminOrganizationBadge {
|
||||
label: string;
|
||||
badgeClass: string;
|
||||
@@ -290,6 +294,20 @@ export const getNoActiveProLicenseState = (): LicenseLoadingStateCopy => ({
|
||||
text: 'No Pro license is active.',
|
||||
});
|
||||
|
||||
export const getTrialEndedProLicenseNotice = (): LicenseActionNotice => ({
|
||||
tone: 'border-red-200 dark:border-red-900 bg-red-50 dark:bg-red-900 text-red-900 dark:text-red-100',
|
||||
title: 'Your Pro trial has ended',
|
||||
body: 'Upgrade to keep Pro features.',
|
||||
actionLabel: 'View Pro plans',
|
||||
});
|
||||
|
||||
export const getInactiveProUpsellNotice = (): LicenseActionNotice => ({
|
||||
tone: 'border-amber-200 dark:border-amber-800 bg-amber-50 dark:bg-amber-900 text-amber-800 dark:text-amber-200',
|
||||
title: 'Upgrade to Pro',
|
||||
body: 'Unlock Pulse Patrol, alert analysis, auto-fix, and more.',
|
||||
actionLabel: 'View Pro plans',
|
||||
});
|
||||
|
||||
export const getOrganizationBillingLicenseStatusLabel = (
|
||||
status?: Pick<LicenseStatus, 'valid' | 'in_grace_period'> | null,
|
||||
): string => {
|
||||
|
||||
Reference in New Issue
Block a user