docs & ux improvmeents

This commit is contained in:
IGNY8 VPS (Salman)
2025-12-25 20:31:58 +00:00
parent 90e6e96b2b
commit 4bffede052
247 changed files with 6869 additions and 53517 deletions

View File

@@ -129,7 +129,7 @@ export default function SignUpForm({ planDetails: planDetailsProp, planLoading:
<p className="text-sm text-gray-500 dark:text-gray-400">
{planSlug && paidPlans.includes(planSlug)
? `You're signing up for the ${planSlug} plan. You'll be taken to billing to complete payment.`
: "No credit card required. 100 AI credits to get started."}
: "No credit card required. Start creating content today."}
</p>
</div>
<div>

View File

@@ -275,7 +275,7 @@ export default function SignUpFormEnhanced({ planDetails: planDetailsProp, planL
<p className="text-sm text-gray-500 dark:text-gray-400">
{isPaidPlan
? `Complete the ${totalSteps}-step process to activate your subscription.`
: 'No credit card required. 1000 AI credits to get started.'}
: 'No credit card required. Start creating content today.'}
</p>
</div>

View File

@@ -226,7 +226,7 @@ export default function SignUpFormSimplified({ planDetails: planDetailsProp, pla
<p className="text-sm text-gray-500 dark:text-gray-400">
{isPaidPlan
? 'Complete your registration and select a payment method.'
: 'No credit card required. 1000 AI credits to get started.'}
: 'No credit card required. Start creating content today.'}
</p>
</div>

View File

@@ -19,7 +19,7 @@ export default function BillingBalancePanel() {
if (loading && !balance) {
return (
<div className="p-4">
<div className="text-gray-500">Loading credit balance...</div>
<div className="text-gray-500">Loading content usage...</div>
</div>
);
}
@@ -28,19 +28,19 @@ export default function BillingBalancePanel() {
<div className="p-4 space-y-6">
<div className="flex items-center justify-between mb-4">
<div>
<h2 className="text-lg font-semibold text-gray-900 dark:text-white">Credit Balance</h2>
<p className="text-sm text-gray-600 dark:text-gray-400 mt-1">Manage your AI credits and subscription status</p>
<h2 className="text-lg font-semibold text-gray-900 dark:text-white">Usage Overview</h2>
<p className="text-sm text-gray-600 dark:text-gray-400 mt-1">Track your content generation and plan status</p>
</div>
<Link to="/account/purchase-credits">
<Link to="/account/plans-and-billing">
<Button variant="primary" startIcon={<DollarLineIcon className="w-4 h-4" />}>
Purchase Credits
Manage Plan
</Button>
</Link>
</div>
{error && !balance && (
<div className="p-4 rounded-lg border border-amber-200 bg-amber-50 text-amber-800 dark:border-amber-800 dark:bg-amber-900/20 dark:text-amber-200">
Balance unavailable. {error}
Usage unavailable. {error}
<div className="mt-3">
<Button variant="outline" size="sm" onClick={loadBalance}>
Retry
@@ -53,23 +53,23 @@ export default function BillingBalancePanel() {
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<Card className="p-6">
<div className="flex items-center justify-between mb-2">
<h3 className="text-sm font-medium text-gray-600 dark:text-gray-400">Current Balance</h3>
<h3 className="text-sm font-medium text-gray-600 dark:text-gray-400">Content This Month</h3>
</div>
<div className="text-3xl font-bold text-gray-900 dark:text-white">
{(balance?.credits ?? 0).toLocaleString()}
{(balance?.credits_remaining ?? balance?.credits ?? 0).toLocaleString()}
</div>
<p className="text-sm text-gray-500 dark:text-gray-400 mt-2">Available credits</p>
<p className="text-sm text-gray-500 dark:text-gray-400 mt-2">Content pieces remaining</p>
</Card>
<Card className="p-6">
<div className="flex items-center justify-between mb-2">
<h3 className="text-sm font-medium text-gray-600 dark:text-gray-400">Subscription Plan</h3>
<h3 className="text-sm font-medium text-gray-600 dark:text-gray-400">Current Plan</h3>
</div>
<div className="text-3xl font-bold text-gray-900 dark:text-white">
(balance as any)?.subscription_plan || user?.account?.plan?.name || 'None'
</div>
<p className="text-sm text-gray-500 dark:text-gray-400 mt-2">
{(balance?.plan_credits_per_month ?? 0) ? `${(balance?.plan_credits_per_month ?? 0).toLocaleString()} credits/month` : 'No subscription'}
{(balance?.plan_credits_per_month ?? 0) ? `${(balance?.plan_credits_per_month ?? 0).toLocaleString()} content pieces/month` : 'No subscription'}
</p>
</Card>

View File

@@ -12,17 +12,17 @@ export default function CreditBalanceWidget() {
if (loading && !balance) {
return (
<ComponentCard title="Credit Balance" desc="Loading...">
<div className="animate-pulse">Loading credit balance...</div>
<ComponentCard title="Content Usage" desc="Loading...">
<div className="animate-pulse">Loading content usage...</div>
</ComponentCard>
);
}
if (error && !balance) {
return (
<ComponentCard title="Credit Balance" desc="Balance unavailable">
<ComponentCard title="Content Usage" desc="Usage unavailable">
<div className="text-sm text-red-600 dark:text-red-400 mb-3">
Balance unavailable. Please retry.
Usage unavailable. Please retry.
</div>
<Button variant="outline" size="sm" onClick={loadBalance}>
Retry
@@ -38,11 +38,11 @@ export default function CreditBalanceWidget() {
: 0;
return (
<ComponentCard title="Credit Balance" desc="Current credit status and usage">
<ComponentCard title="Content Usage" desc="Monthly content allowance">
<div className="space-y-4">
<div>
<div className="flex justify-between items-center mb-2">
<span className="text-sm text-gray-600 dark:text-gray-400">Current Credits</span>
<span className="text-sm text-gray-600 dark:text-gray-400">Content Pieces</span>
<span className="text-2xl font-bold text-primary">{balance.credits}</span>
</div>
</div>
@@ -69,7 +69,7 @@ export default function CreditBalanceWidget() {
</div>
{error && (
<div className="mt-2 text-xs text-amber-600 dark:text-amber-400">
Balance may be outdated. {error}
Usage may be outdated. {error}
</div>
)}
</div>

View File

@@ -36,7 +36,7 @@ export default function UsageChartWidget() {
return (
<ComponentCard
title="Usage Summary"
desc="Credit usage breakdown by operation and model"
desc="Usage breakdown by operation and model"
>
<div className="space-y-4">
<div className="flex justify-end items-center mb-4">
@@ -53,7 +53,7 @@ export default function UsageChartWidget() {
<div className="grid grid-cols-2 gap-4">
<div>
<div className="text-sm text-gray-600 dark:text-gray-400">Total Credits Used</div>
<div className="text-sm text-gray-600 dark:text-gray-400">Content Created</div>
<div className="text-2xl font-bold">{usageSummary.total_credits_used}</div>
</div>
<div>
@@ -68,7 +68,7 @@ export default function UsageChartWidget() {
{usageSummary.by_operation && Object.entries(usageSummary.by_operation).map(([op, stats]) => (
<div key={op} className="flex justify-between items-center text-sm">
<span className="capitalize">{op.replace('_', ' ')}</span>
<span className="font-medium">{stats.credits} credits (${(Number(stats.cost) || 0).toFixed(2)})</span>
<span className="font-medium">{stats.credits} operations (${(Number(stats.cost) || 0).toFixed(2)})</span>
</div>
))}
{(!usageSummary.by_operation || Object.keys(usageSummary.by_operation || {}).length === 0) && (

View File

@@ -194,7 +194,7 @@ export function PricingTable({ variant = '1', title, plans, showToggle = false,
<li className="flex items-start gap-2">
<Check className="w-4 h-4 text-blue-500 flex-shrink-0 mt-0.5" />
<span className="text-xs text-gray-600 dark:text-gray-400">
{plan.included_credits.toLocaleString()} Credits/month
{plan.included_credits.toLocaleString()} Content pieces/month
</span>
</li>
)}

View File

@@ -123,22 +123,26 @@ const LayoutContent: React.FC = () => {
return;
}
// Determine accent color based on credit level
// Determine accent color based on content pieces remaining
const remaining = balance.credits_remaining ?? balance.credits;
const total = balance.plan_credits_per_month ?? 0;
const usagePercent = total > 0 ? (remaining / total) * 100 : 100;
let accentColor: 'blue' | 'green' | 'amber' | 'purple' = 'blue';
if (balance.credits > 1000) {
if (usagePercent > 50) {
accentColor = 'green';
} else if (balance.credits > 100) {
} else if (usagePercent > 20) {
accentColor = 'blue';
} else if (balance.credits > 0) {
} else if (usagePercent > 0) {
accentColor = 'amber';
} else {
accentColor = 'purple';
}
// Set credit balance (single metric with label "Credits" - HeaderMetricsContext will merge it)
// Set content balance - show as "X/Y Content" for simplicity
setMetrics([{
label: 'Credits',
value: balance.credits,
label: 'Content',
value: total > 0 ? `${remaining}/${total}` : remaining,
accentColor,
}]);
}, [balance, isAuthenticated, setMetrics]);

View File

@@ -117,7 +117,7 @@ const Pricing: React.FC = () => {
{ feature: "Advanced AI Parameter Tuning", starter: true, growth: true, scale: true },
{ feature: "BILLING & INVOICING", starter: null, growth: null, scale: null, isCategory: true },
{ feature: "Credit Balance Dashboard", starter: true, growth: true, scale: true },
{ feature: "Usage Dashboard", starter: true, growth: true, scale: true },
{ feature: "Usage Analytics", starter: true, growth: true, scale: true },
{ feature: "Invoice Management", starter: true, growth: true, scale: true },
{ feature: "Multiple Payment Methods", starter: true, growth: true, scale: true },

View File

@@ -245,7 +245,7 @@ const AutomationPage: React.FC = () => {
const handleRunNow = async () => {
if (!activeSite) return;
if (estimate && !estimate.sufficient) {
toast.error(`Insufficient credits. Need ~${estimate.estimated_credits}, you have ${estimate.current_balance}`);
toast.error(`Content limit reached. This run needs ~${estimate.estimated_credits} pieces, you have ${estimate.current_balance} remaining.`);
return;
}
try {
@@ -453,9 +453,9 @@ const AutomationPage: React.FC = () => {
<div className="h-4 w-px bg-white/25"></div>
<div className="text-sm text-white/90">
<span className="font-medium">Est:</span>{' '}
<span className="font-semibold text-white">{estimate?.estimated_credits || 0} credits</span>
<span className="font-semibold text-white">{estimate?.estimated_credits || 0} content pieces</span>
{estimate && !estimate.sufficient && (
<span className="ml-1 text-white/90 font-semibold">(Low)</span>
<span className="ml-1 text-white/90 font-semibold">(Limit reached)</span>
)}
</div>
</div>

View File

@@ -6,20 +6,7 @@ import { getCreditBalance, CreditBalance } from '../../services/billing.api';
import { Card } from '../../components/ui/card';
import Badge from '../../components/ui/badge/Badge';
import Button from '../../components/ui/button/Button';
import { DollarLineIcon } from '../../icons';
// Credit costs per operation (Phase 0: Credit-only system)
const CREDIT_COSTS: Record<string, { cost: number | string; description: string }> = {
clustering: { cost: 10, description: 'Per clustering request' },
idea_generation: { cost: 15, description: 'Per cluster → ideas request' },
content_generation: { cost: '1 per 100 words', description: 'Per 100 words generated' },
image_prompt_extraction: { cost: 2, description: 'Per content piece' },
image_generation: { cost: 5, description: 'Per image generated' },
linking: { cost: 8, description: 'Per content piece' },
optimization: { cost: '1 per 200 words', description: 'Per 200 words optimized' },
site_structure_generation: { cost: 50, description: 'Per site blueprint' },
site_page_generation: { cost: 20, description: 'Per page generated' },
};
import { ArrowUpIcon } from '../../icons';
export default function Credits() {
const toast = useToast();
@@ -36,7 +23,7 @@ export default function Credits() {
const data = await getCreditBalance();
setBalance(data);
} catch (error: any) {
toast.error(`Failed to load credit balance: ${error.message}`);
toast.error(`Failed to load content usage: ${error.message}`);
} finally {
setLoading(false);
}
@@ -45,7 +32,7 @@ export default function Credits() {
if (loading) {
return (
<div className="p-6">
<PageMeta title="Credits" />
<PageMeta title="Content Usage" />
<div className="flex items-center justify-center h-64">
<div className="text-gray-500">Loading...</div>
</div>
@@ -55,15 +42,15 @@ export default function Credits() {
return (
<div className="p-6">
<PageMeta title="Credits" />
<PageMeta title="Content Usage" />
<div className="flex items-center justify-between mb-6">
<div>
<h1 className="text-2xl font-bold text-gray-900 dark:text-white">Credit Balance</h1>
<p className="text-gray-600 dark:text-gray-400 mt-1">Manage your AI credits and usage</p>
<h1 className="text-2xl font-bold text-gray-900 dark:text-white">Content Usage</h1>
<p className="text-gray-600 dark:text-gray-400 mt-1">Track your monthly content allowance</p>
</div>
<Link to="/account/purchase-credits">
<Button variant="primary" startIcon={<DollarLineIcon className="w-4 h-4" />}>
Purchase Credits
<Link to="/account/plans">
<Button variant="primary" startIcon={<ArrowUpIcon className="w-4 h-4" />}>
Manage Plan
</Button>
</Link>
</div>
@@ -72,23 +59,23 @@ export default function Credits() {
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<Card className="p-6">
<div className="flex items-center justify-between mb-2">
<h3 className="text-sm font-medium text-gray-600 dark:text-gray-400">Current Balance</h3>
<h3 className="text-sm font-medium text-gray-600 dark:text-gray-400">Content Remaining</h3>
</div>
<div className="text-3xl font-bold text-gray-900 dark:text-white">
{(balance?.credits ?? 0).toLocaleString()}
</div>
<p className="text-sm text-gray-500 dark:text-gray-400 mt-2">Available credits</p>
<p className="text-sm text-gray-500 dark:text-gray-400 mt-2">Pieces available this month</p>
</Card>
<Card className="p-6">
<div className="flex items-center justify-between mb-2">
<h3 className="text-sm font-medium text-gray-600 dark:text-gray-400">Subscription Plan</h3>
<h3 className="text-sm font-medium text-gray-600 dark:text-gray-400">Current Plan</h3>
</div>
<div className="text-3xl font-bold text-gray-900 dark:text-white">
{(balance as any)?.subscription_plan || 'None'}
</div>
<p className="text-sm text-gray-500 dark:text-gray-400 mt-2">
{(balance?.plan_credits_per_month ?? 0) ? `${(balance?.plan_credits_per_month ?? 0).toLocaleString()} credits/month` : 'No subscription'}
{(balance?.plan_credits_per_month ?? 0) ? `${(balance?.plan_credits_per_month ?? 0).toLocaleString()} content pieces/month` : 'No subscription'}
</p>
</Card>
@@ -106,18 +93,42 @@ export default function Credits() {
</div>
)}
{/* Credit Costs Reference */}
{/* What's Included */}
<div className="mt-8">
<Card className="p-6">
<h2 className="text-lg font-semibold text-gray-800 dark:text-white mb-4">Credit Costs per Operation</h2>
<h2 className="text-lg font-semibold text-gray-800 dark:text-white mb-4">What's Included Per Content Piece</h2>
<p className="text-sm text-gray-600 dark:text-gray-400 mb-4">
Understanding how credits are consumed for each operation type
Every content piece you create includes all of these features
</p>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{Object.entries(CREDIT_COSTS).map(([operation, info]) => (
<div key={operation} className="flex items-start justify-between p-3 bg-gray-50 dark:bg-gray-800 rounded-lg">
<div className="flex-1">
<div className="font-medium text-gray-900 dark:text-white capitalize">
{[
{ name: 'AI Keyword Clustering', desc: 'Smart keyword grouping' },
{ name: 'AI Content Ideas', desc: 'Topic suggestions from keywords' },
{ name: 'AI Content Writing', desc: '1000-2000 word articles' },
{ name: 'AI Images', desc: '1 featured + 2 in-article images' },
{ name: 'Internal Linking', desc: 'Smart link suggestions' },
{ name: 'SEO Optimization', desc: 'Meta tags & readability' },
{ name: 'WordPress Publishing', desc: 'Direct publish to your site' },
{ name: 'Multiple Formats', desc: 'Pages, articles, product content' },
].map((feature) => (
<div key={feature.name} className="flex items-start gap-3 p-3 bg-gray-50 dark:bg-gray-800 rounded-lg">
<div className="w-5 h-5 rounded-full bg-green-100 dark:bg-green-900 flex items-center justify-center flex-shrink-0 mt-0.5">
<svg className="w-3 h-3 text-green-600 dark:text-green-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
</svg>
</div>
<div>
<div className="font-medium text-gray-900 dark:text-white">{feature.name}</div>
<div className="text-sm text-gray-600 dark:text-gray-400">{feature.desc}</div>
</div>
</div>
))}
</div>
</Card>
</div>
</div>
);
}
{operation.replace(/_/g, ' ')}
</div>
<div className="text-sm text-gray-600 dark:text-gray-400 mt-1">

View File

@@ -8,6 +8,7 @@ import ComponentCard from "../../components/common/ComponentCard";
import PageHeader from "../../components/common/PageHeader";
import WorkflowGuide from "../../components/onboarding/WorkflowGuide";
import { useOnboardingStore } from "../../store/onboardingStore";
import { useBillingStore } from "../../store/billingStore";
import { Card } from "../../components/ui/card";
import { ProgressBar } from "../../components/ui/progress";
import { ApexOptions } from "apexcharts";
@@ -240,6 +241,7 @@ export default function Home() {
const { activeSector } = useSectorStore();
const { isGuideDismissed, showGuide, loadFromBackend } = useOnboardingStore();
const { user } = useAuthStore();
const { balance, loadBalance } = useBillingStore();
const [insights, setInsights] = useState<AppInsights | null>(null);
const [loading, setLoading] = useState(true);
@@ -272,6 +274,7 @@ export default function Home() {
// Load sites and guide state
useEffect(() => {
loadSites();
loadBalance();
loadFromBackend().catch(() => {
// Silently fail - local state will be used
});
@@ -890,15 +893,15 @@ export default function Home() {
/>
</div>
{/* Credit Balance & Usage - Improved Design */}
{/* Content Usage - Simplified Design */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
<ComponentCard title="Credit Balance" desc="Current credit status and usage">
<ComponentCard title="Content This Month" desc="Your content generation allowance">
<div className="space-y-6">
<div className="text-center py-4">
<div className="text-4xl font-bold text-brand-600 dark:text-brand-400 mb-2">
{user?.account?.credits?.toLocaleString() || '0'}
{balance?.credits_remaining?.toLocaleString() || balance?.credits?.toLocaleString() || '0'}
</div>
<div className="text-sm text-gray-600 dark:text-gray-400">Current Credits</div>
<div className="text-sm text-gray-600 dark:text-gray-400">Content Pieces Remaining</div>
</div>
<div className="space-y-4">
@@ -906,13 +909,13 @@ export default function Home() {
<div className="flex justify-between items-center mb-2">
<span className="text-sm font-medium text-gray-700 dark:text-gray-300">Used This Month</span>
<span className="text-sm font-semibold">
{user?.account?.credits ? '547' : '0'} / {user?.account?.credits ? '1000' : '0'}
{balance?.credits_used_this_month || '0'} / {balance?.plan_credits_per_month || '0'}
</span>
</div>
<div className="w-full bg-gray-200 rounded-full h-3 dark:bg-gray-700">
<div
className="bg-brand-500 h-3 rounded-full transition-all"
style={{ width: `${user?.account?.credits ? 54.7 : 0}%` }}
style={{ width: `${balance?.plan_credits_per_month ? ((balance?.credits_used_this_month || 0) / balance.plan_credits_per_month) * 100 : 0}%` }}
/>
</div>
</div>
@@ -921,7 +924,7 @@ export default function Home() {
<div className="flex justify-between items-center">
<span className="text-sm font-medium text-gray-700 dark:text-gray-300">Remaining</span>
<span className="text-lg font-bold text-green-600 dark:text-green-400">
{user?.account?.credits?.toLocaleString() || '0'}
{balance?.credits_remaining?.toLocaleString() || balance?.credits?.toLocaleString() || '0'}
</span>
</div>
</div>

View File

@@ -107,8 +107,8 @@ export default function Help() {
answer: "The Thinker module manages AI prompts, author profiles, and content strategies. Use it to customize how AI generates content, define writing styles, and create reusable prompt templates."
},
{
question: "How do I manage my credits?",
answer: "Go to Billing &gt; Credits to view your credit balance, purchase more credits, and see credit usage. Credits are used for AI operations like content generation, clustering, and image generation."
question: "How does content usage work?",
answer: "Each plan includes a monthly content allowance. Creating articles, generating ideas, and producing images all count toward your monthly limit. View your usage in Settings > Plans & Billing. Your allowance resets at the start of each billing period."
},
{
question: "Can I export my data?",

View File

@@ -12,7 +12,7 @@ import { useSectorStore } from '../../store/sectorStore';
interface OptimizerStats {
totalOptimized: number;
averageScoreImprovement: number;
totalCreditsUsed: number;
totalOperations: number;
contentWithScores: number;
contentWithoutScores: number;
}
@@ -53,7 +53,7 @@ export default function OptimizerDashboard() {
setStats({
totalOptimized,
averageScoreImprovement: parseFloat(averageScoreImprovement.toFixed(1)),
totalCreditsUsed: 0, // Would need to fetch from optimization tasks
totalOperations: 0, // Would need to fetch from optimization tasks
contentWithScores: contentWithScores.length,
contentWithoutScores: content.length - contentWithScores.length,
});
@@ -118,9 +118,9 @@ export default function OptimizerDashboard() {
/>
<EnhancedMetricCard
title="Credits Used"
value={stats.totalCreditsUsed.toString()}
subtitle="Total credits for optimization"
title="Optimizations"
value={stats.totalOperations.toString()}
subtitle="Total optimization runs"
icon={<BoltIcon className="w-6 h-6" />}
accentColor="orange"
onClick={() => navigate('/optimizer/content')}

View File

@@ -2,10 +2,10 @@ import { useEffect, useMemo, useState } from "react";
import { useLocation, useNavigate, Link } from "react-router-dom";
import { useAuthStore } from "../store/authStore";
const PLAN_COPY: Record<string, { name: string; price: string; credits: string }> = {
starter: { name: "Starter", price: "$89/mo", credits: "1,000 credits/month" },
growth: { name: "Growth", price: "$139/mo", credits: "2,000 credits/month" },
scale: { name: "Scale", price: "$229/mo", credits: "4,000 credits/month" },
const PLAN_COPY: Record<string, { name: string; price: string; content: string }> = {
starter: { name: "Starter", price: "$49/mo", content: "50 content pieces/month" },
growth: { name: "Growth", price: "$149/mo", content: "200 content pieces/month" },
scale: { name: "Scale", price: "$349/mo", content: "500 content pieces/month" },
};
export default function Payment() {
@@ -68,7 +68,7 @@ export default function Payment() {
<div className="rounded-xl border border-slate-200 bg-slate-50 p-4">
<h2 className="text-lg font-semibold text-slate-900">{plan.name}</h2>
<p className="text-slate-700">{plan.price}</p>
<p className="text-sm text-slate-600">{plan.credits}</p>
<p className="text-sm text-slate-600">{plan.content}</p>
<p className="text-xs text-amber-700 mt-2">
Payment is completed offline (bank transfer). Submit your email and reference below; we will verify and activate your account.
</p>

View File

@@ -1,6 +1,6 @@
/**
* Credits & Billing Page
* User-facing credits usage, transactions, and billing information
* Usage & Billing Page
* User-facing usage overview, transactions, and billing information
*/
import React, { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
@@ -67,7 +67,7 @@ const CreditsAndBilling: React.FC = () => {
if (loading) {
return (
<div className="p-6">
<PageMeta title="Credits & Billing" description="Manage your credits and billing" />
<PageMeta title="Usage & Billing" description="View your usage and billing" />
<div className="text-center py-12">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-primary-600 mx-auto"></div>
<p className="mt-4 text-gray-600 dark:text-gray-400">Loading billing data...</p>
@@ -78,32 +78,32 @@ const CreditsAndBilling: React.FC = () => {
return (
<div className="p-6">
<PageMeta title="Credits & Billing" description="Manage your credits and billing" />
<PageMeta title="Usage & Billing" description="View your usage and billing" />
<div className="flex items-center justify-between mb-6">
<div>
<h1 className="text-3xl font-bold text-gray-900 dark:text-white">Credits & Billing</h1>
<h1 className="text-3xl font-bold text-gray-900 dark:text-white">Usage & Billing</h1>
<p className="text-gray-600 dark:text-gray-400 mt-1">
Manage your credits, view transactions, and monitor usage
View your content usage and billing history
</p>
</div>
<Link to="/account/purchase-credits">
<Link to="/account/plans">
<Button variant="primary">
Purchase Credits
Manage Plan
</Button>
</Link>
</div>
{/* Credit Balance Cards */}
{/* Balance Cards */}
<div className="grid grid-cols-1 md:grid-cols-4 gap-4 mb-6">
<EnhancedMetricCard
title="Current Balance"
title="Content Remaining"
value={balance?.balance || 0}
icon={<BoltIcon />}
accentColor="orange"
/>
<EnhancedMetricCard
title="Monthly Credits"
title="Monthly Allowance"
value={balance?.monthly_credits || 0}
subtitle={balance?.subscription_plan || 'No plan'}
icon={<CheckCircleIcon />}

View File

@@ -51,23 +51,22 @@ const formatWordCount = (num: number): string => {
return num.toString();
};
// Extract major features from plan data - SAME AS MARKETING PAGE
// Extract major features from plan data - SIMPLIFIED VERSION
const extractFeatures = (plan: Plan): string[] => {
const features: string[] = [];
// Content pieces is the main metric now (using included_credits as content pieces)
if (plan.included_credits) features.push(`${formatNumber(plan.included_credits)} Pages/Articles per month`);
if (plan.max_sites) features.push(`${plan.max_sites === 999999 ? 'Unlimited' : plan.max_sites} Site${plan.max_sites > 1 ? 's' : ''}`);
if (plan.max_users) features.push(`${plan.max_users} Team User${plan.max_users > 1 ? 's' : ''}`);
if (plan.included_credits) features.push(`${formatNumber(plan.included_credits)} Monthly Credits`);
if (plan.monthly_word_count_limit) features.push(`${formatWordCount(plan.monthly_word_count_limit)} Words/Month`);
if (plan.max_clusters) features.push(`${plan.max_clusters} AI Keyword Clusters`);
if (plan.max_content_ideas) features.push(`${formatNumber(plan.max_content_ideas)} Content Ideas`);
if (plan.monthly_image_count) {
// Try to split into basic/premium if data available, otherwise show total
const basicImages = Math.floor(plan.monthly_image_count * 0.8);
const premiumImages = Math.floor(plan.monthly_image_count * 0.2);
features.push(`${formatNumber(basicImages)} Basic / ${formatNumber(premiumImages)} Premium Images`);
}
if (plan.daily_image_generation_limit) features.push(`${formatNumber(plan.daily_image_generation_limit)} Image Prompts`);
// Include features as checkmarks
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');
return features;
};

View File

@@ -1,6 +1,6 @@
/**
* Plans & Billing Page - Refactored for Better UX
* Organized tabs: Current Plan, Plan Limits, Credits, Purchase Credits, Billing History
* Organized tabs: Current Plan, Plan Limits, Usage, Upgrade Plan, Billing History
*/
import { useState, useEffect, useRef } from 'react';
@@ -15,7 +15,7 @@ import { useToast } from '../../components/ui/toast/ToastContainer';
import { PricingPlan } from '../../components/ui/pricing-table';
import PricingTable1 from '../../components/ui/pricing-table/pricing-table-1';
import CreditCostBreakdownPanel from '../../components/billing/CreditCostBreakdownPanel';
import CreditCostsPanel from '../../components/billing/CreditCostsPanel';
// import CreditCostsPanel from '../../components/billing/CreditCostsPanel'; // Hidden from regular users
import UsageLimitsPanel from '../../components/billing/UsageLimitsPanel';
import { convertToPricingPlan } from '../../utils/pricingHelpers';
import {
@@ -345,9 +345,8 @@ export default function PlansAndBillingPage() {
const tabs = [
{ id: 'plan' as TabType, label: 'Current Plan', icon: <Package className="w-4 h-4" /> },
{ id: 'limits' as TabType, label: 'Plan Limits', icon: <BarChart3 className="w-4 h-4" /> },
{ id: 'credits' as TabType, label: 'Credits', icon: <TrendingUp className="w-4 h-4" /> },
{ id: 'upgrade' as TabType, label: 'Purchase/Upgrade', icon: <Wallet className="w-4 h-4" /> },
{ id: 'limits' as TabType, label: 'Usage', icon: <BarChart3 className="w-4 h-4" /> },
{ id: 'upgrade' as TabType, label: 'Upgrade Plan', icon: <Wallet className="w-4 h-4" /> },
{ id: 'invoices' as TabType, label: 'History', icon: <FileText className="w-4 h-4" /> },
];
@@ -356,7 +355,7 @@ export default function PlansAndBillingPage() {
<div className="mb-6">
<h1 className="text-2xl font-bold text-gray-900 dark:text-white">Your Subscription</h1>
<p className="text-gray-600 dark:text-gray-400 mt-1">
Manage your plan and payments - View what's included, upgrade, or buy more credits
Manage your plan and view usage
</p>
</div>
@@ -477,7 +476,7 @@ export default function PlansAndBillingPage() {
onClick={() => setActiveTab('upgrade')}
startIcon={<ArrowUpCircle className="w-4 h-4" />}
>
Purchase Credits
Upgrade Plan
</Button>
<Button
variant="outline"
@@ -597,22 +596,22 @@ export default function PlansAndBillingPage() {
</div>
)}
{/* Credits Overview Tab */}
{/* Usage Overview Tab */}
{activeTab === 'credits' && (
<div className="space-y-6">
{/* Credit Balance Cards */}
{/* Usage Cards */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<Card className="p-6 bg-gradient-to-br from-brand-50 to-brand-100 dark:from-brand-900/20 dark:to-brand-800/10 border-brand-200 dark:border-brand-700">
<div className="flex items-center gap-3 mb-3">
<div className="p-2 bg-brand-500 rounded-lg">
<Wallet className="w-5 h-5 text-white" />
</div>
<div className="text-sm font-medium text-brand-700 dark:text-brand-300">Current Balance</div>
<div className="text-sm font-medium text-brand-700 dark:text-brand-300">Content Remaining</div>
</div>
<div className="text-4xl font-bold text-brand-600 dark:text-brand-400">
{creditBalance?.credits.toLocaleString() || 0}
</div>
<div className="text-sm text-brand-600 dark:text-brand-400 mt-2">credits available</div>
<div className="text-sm text-brand-600 dark:text-brand-400 mt-2">pieces available</div>
</Card>
<Card className="p-6 bg-gradient-to-br from-red-50 to-red-100 dark:from-red-900/20 dark:to-red-800/10 border-red-200 dark:border-red-700">
@@ -689,19 +688,7 @@ export default function PlansAndBillingPage() {
</div>
</Card>
{/* Credit Cost Breakdown */}
<div className="mt-8 pt-8 border-t border-gray-200 dark:border-gray-700">
<div className="mb-6">
<h2 className="text-xl font-semibold text-gray-900 dark:text-white">Credit Cost Analytics</h2>
<p className="text-gray-600 dark:text-gray-400">Detailed breakdown of credit usage by operation type</p>
</div>
<CreditCostBreakdownPanel />
</div>
{/* Credit Costs Reference */}
<div className="mt-8 pt-8 border-t border-gray-200 dark:border-gray-700">
<CreditCostsPanel />
</div>
{/* Usage Analytics - removed detailed credit breakdown to simplify user view */}
</div>
)}
@@ -765,7 +752,7 @@ export default function PlansAndBillingPage() {
</Card>
</div>
{/* Purchase Additional Credits Section */}
{/* Purchase Additional Credits Section - Hidden from regular users
<div className="mt-12 pt-8 border-t-2 border-gray-200 dark:border-gray-700">
<div className="mb-6">
<h2 className="text-xl font-semibold mb-2 text-gray-900 dark:text-white">Purchase Additional Credits</h2>
@@ -780,14 +767,14 @@ export default function PlansAndBillingPage() {
<Wallet className="w-6 h-6 text-white" />
</div>
<div>
<div className="text-sm text-gray-600 dark:text-gray-400">Current Credit Balance</div>
<div className="text-sm text-gray-600 dark:text-gray-400">Content Remaining</div>
<div className="text-3xl font-bold text-gray-900 dark:text-white">
{creditBalance?.credits.toLocaleString() || 0}
</div>
</div>
</div>
<div className="text-right">
<div className="text-sm text-gray-600 dark:text-gray-400">Monthly Allocation</div>
<div className="text-sm text-gray-600 dark:text-gray-400">Monthly Allowance</div>
<div className="text-xl font-bold text-brand-600 dark:text-brand-400">
{creditBalance?.plan_credits_per_month.toLocaleString() || 0}
</div>
@@ -795,7 +782,8 @@ export default function PlansAndBillingPage() {
</div>
</Card>
{/* Credit Packages Grid */}
{/* Credit Packages Grid - Hidden from regular users */}
{/*
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 mt-6">
{packages.map((pkg) => (
<Card
@@ -867,6 +855,7 @@ export default function PlansAndBillingPage() {
</Card>
)}
</div>
*/}
</div>
)}