pricign plans updates
This commit is contained in:
93
frontend/src/utils/pricingHelpers.ts
Normal file
93
frontend/src/utils/pricingHelpers.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* Shared pricing utilities for converting backend Plan data to PricingPlan format
|
||||
* Used by both marketing pricing page and app's plans page
|
||||
*/
|
||||
|
||||
import { PricingPlan } from '../components/ui/pricing-table';
|
||||
|
||||
export interface Plan {
|
||||
id: number;
|
||||
name: string;
|
||||
slug?: string;
|
||||
price: number | string;
|
||||
original_price?: number | string;
|
||||
annual_discount_percent?: number;
|
||||
is_featured?: boolean;
|
||||
max_sites?: number;
|
||||
max_users?: number;
|
||||
max_keywords?: number;
|
||||
max_clusters?: number;
|
||||
max_content_ideas?: number;
|
||||
max_content_words?: number;
|
||||
max_images_basic?: number;
|
||||
max_images_premium?: number;
|
||||
max_image_prompts?: number;
|
||||
included_credits?: number;
|
||||
}
|
||||
|
||||
export const formatNumber = (num: number | undefined | null): string => {
|
||||
if (!num || num === 0) return '0';
|
||||
if (num >= 1000000) return `${(num / 1000).toFixed(0)}M`;
|
||||
if (num >= 1000) return `${(num / 1000).toFixed(0)}K`;
|
||||
return num.toString();
|
||||
};
|
||||
|
||||
export const convertToPricingPlan = (plan: Plan): PricingPlan => {
|
||||
const monthlyPrice = typeof plan.price === 'number' ? plan.price : parseFloat(String(plan.price || 0));
|
||||
const features: string[] = [];
|
||||
|
||||
// Dynamic counts - shown with numbers from backend
|
||||
if (plan.max_content_ideas) {
|
||||
features.push(`**${formatNumber(plan.max_content_ideas)} Pages/Articles per month**`);
|
||||
}
|
||||
if (plan.max_sites) {
|
||||
features.push(`${plan.max_sites === 999999 ? 'Unlimited' : formatNumber(plan.max_sites)} Site${plan.max_sites > 1 && plan.max_sites !== 999999 ? 's' : ''}`);
|
||||
}
|
||||
if (plan.max_users) {
|
||||
features.push(`${formatNumber(plan.max_users)} Team User${plan.max_users > 1 ? 's' : ''}`);
|
||||
}
|
||||
|
||||
// Features - shown with checkmarks only (no counts)
|
||||
features.push('AI Keyword Clustering');
|
||||
features.push('AI Content Ideas');
|
||||
features.push('AI Image Generation');
|
||||
features.push('Internal Linking');
|
||||
features.push('SEO Optimization');
|
||||
features.push('WordPress Publishing');
|
||||
features.push('Pre-Researched High-Opportunity Keywords');
|
||||
|
||||
// Priority Support - only for Scale plan (excluded for others)
|
||||
const planName = plan.name.toLowerCase();
|
||||
if (planName.includes('scale')) {
|
||||
features.push('Priority Support');
|
||||
} else {
|
||||
features.push('!Priority Support');
|
||||
}
|
||||
|
||||
// Custom descriptions based on plan name
|
||||
let description = `Perfect for ${plan.name.toLowerCase()} needs`;
|
||||
if (plan.name.toLowerCase().includes('free')) {
|
||||
description = 'Explore core features risk free';
|
||||
} else if (plan.name.toLowerCase().includes('starter')) {
|
||||
description = 'Launch SEO workflows for small teams';
|
||||
} else if (plan.name.toLowerCase().includes('growth')) {
|
||||
description = 'Scale content production with confidence';
|
||||
} else if (plan.name.toLowerCase().includes('scale')) {
|
||||
description = 'Enterprise power for high volume growth';
|
||||
}
|
||||
|
||||
return {
|
||||
id: plan.id,
|
||||
slug: plan.slug,
|
||||
name: plan.name,
|
||||
monthlyPrice: monthlyPrice,
|
||||
price: monthlyPrice,
|
||||
originalPrice: plan.original_price ? (typeof plan.original_price === 'number' ? plan.original_price : parseFloat(String(plan.original_price))) : undefined,
|
||||
annualDiscountPercent: plan.annual_discount_percent || 15,
|
||||
period: '/month',
|
||||
description: description,
|
||||
features,
|
||||
buttonText: monthlyPrice === 0 ? 'Free Trial' : 'Choose Plan',
|
||||
highlighted: plan.is_featured || false,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user