Phase 3 - credts, usage, plans app pages #Migrations
This commit is contained in:
79
frontend/src/utils/creditCheck.ts
Normal file
79
frontend/src/utils/creditCheck.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* Credit Check Utilities
|
||||
* Pre-flight credit checks for AI operations
|
||||
*/
|
||||
|
||||
import { getCreditBalance, type CreditBalance } from '../services/billing.api';
|
||||
|
||||
export interface CreditCheckResult {
|
||||
hasEnoughCredits: boolean;
|
||||
availableCredits: number;
|
||||
requiredCredits: number;
|
||||
shortfall: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if account has enough credits for an operation
|
||||
* @param estimatedCredits - Estimated credits needed for the operation
|
||||
* @returns Credit check result with balance info
|
||||
*/
|
||||
export async function checkCreditsBeforeOperation(
|
||||
estimatedCredits: number
|
||||
): Promise<CreditCheckResult> {
|
||||
try {
|
||||
const balance: CreditBalance = await getCreditBalance();
|
||||
const hasEnoughCredits = balance.credits >= estimatedCredits;
|
||||
|
||||
return {
|
||||
hasEnoughCredits,
|
||||
availableCredits: balance.credits,
|
||||
requiredCredits: estimatedCredits,
|
||||
shortfall: Math.max(0, estimatedCredits - balance.credits),
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Failed to check credit balance:', error);
|
||||
// Return pessimistic result on error to prevent operation
|
||||
return {
|
||||
hasEnoughCredits: false,
|
||||
availableCredits: 0,
|
||||
requiredCredits: estimatedCredits,
|
||||
shortfall: estimatedCredits,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Estimated credit costs for common operations
|
||||
* These are estimates - actual costs depend on token usage
|
||||
*/
|
||||
export const ESTIMATED_CREDIT_COSTS = {
|
||||
// Content Generation
|
||||
content_generation_short: 5, // ~500 words
|
||||
content_generation_medium: 10, // ~1000 words
|
||||
content_generation_long: 20, // ~2000+ words
|
||||
|
||||
// Clustering & Planning
|
||||
cluster_keywords: 3, // Per clustering operation
|
||||
generate_content_ideas: 5, // Per batch of ideas
|
||||
|
||||
// Image Generation
|
||||
image_basic: 2, // Basic quality
|
||||
image_premium: 5, // Premium quality
|
||||
|
||||
// SEO Optimization
|
||||
seo_analysis: 2,
|
||||
seo_optimization: 3,
|
||||
|
||||
// Internal Linking
|
||||
internal_linking: 2,
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* Get estimated cost for an operation type
|
||||
*/
|
||||
export function getEstimatedCost(
|
||||
operationType: keyof typeof ESTIMATED_CREDIT_COSTS,
|
||||
quantity: number = 1
|
||||
): number {
|
||||
return ESTIMATED_CREDIT_COSTS[operationType] * quantity;
|
||||
}
|
||||
@@ -16,12 +16,7 @@ export interface Plan {
|
||||
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;
|
||||
max_ahrefs_queries?: number;
|
||||
included_credits?: number;
|
||||
}
|
||||
|
||||
@@ -37,8 +32,8 @@ export const convertToPricingPlan = (plan: Plan): PricingPlan => {
|
||||
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_keywords) {
|
||||
features.push(`**${formatNumber(plan.max_keywords)} Keywords**`);
|
||||
}
|
||||
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' : ''}`);
|
||||
|
||||
Reference in New Issue
Block a user