docs & ux improvmeents
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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) && (
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 > 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?",
|
||||
|
||||
@@ -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')}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 />}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user