From d0e6b342b5d09d04f545c2b6f1f53db7dcd82050 Mon Sep 17 00:00:00 2001 From: "IGNY8 VPS (Salman)" Date: Sun, 16 Nov 2025 19:06:07 +0000 Subject: [PATCH] Phase 0: Update billing pages to show credits and credit costs - Updated Usage page to show only credits and account management limits - Removed plan operation limit displays (planner, writer, images) - Added credit costs reference table showing cost per operation type - Updated limit cards to handle null limits (for current balance display) - Improved UI to focus on credit-only system --- frontend/src/pages/Billing/Usage.tsx | 210 +++++++++++++-------------- 1 file changed, 100 insertions(+), 110 deletions(-) diff --git a/frontend/src/pages/Billing/Usage.tsx b/frontend/src/pages/Billing/Usage.tsx index 64213568..1baa219a 100644 --- a/frontend/src/pages/Billing/Usage.tsx +++ b/frontend/src/pages/Billing/Usage.tsx @@ -5,6 +5,19 @@ import { fetchCreditUsage, CreditUsageLog, fetchUsageLimits, LimitCard } from '. import { Card } from '../../components/ui/card'; import Badge from '../../components/ui/badge/Badge'; +// Credit costs per operation (Phase 0: Credit-only system) +const CREDIT_COSTS: Record = { + 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' }, +}; + export default function Usage() { const toast = useToast(); const [usageLogs, setUsageLogs] = useState([]); @@ -33,13 +46,8 @@ export default function Usage() { try { setLimitsLoading(true); const response = await fetchUsageLimits(); - console.log('Usage limits response:', response); setLimits(response.limits || []); - if (!response.limits || response.limits.length === 0) { - console.warn('No limits data received from API'); - } } catch (error: any) { - console.error('Error loading usage limits:', error); toast.error(`Failed to load usage limits: ${error.message}`); setLimits([]); } finally { @@ -47,120 +55,82 @@ export default function Usage() { } }; - const groupedLimits = { - planner: limits.filter(l => l.category === 'planner'), - writer: limits.filter(l => l.category === 'writer'), - images: limits.filter(l => l.category === 'images'), - ai: limits.filter(l => l.category === 'ai'), - general: limits.filter(l => l.category === 'general'), - }; - - // Debug info - console.log('[Usage Component] Render state:', { - limitsLoading, - limitsCount: limits.length, - groupedLimits, - plannerCount: groupedLimits.planner.length, - writerCount: groupedLimits.writer.length, - imagesCount: groupedLimits.images.length, - aiCount: groupedLimits.ai.length, - generalCount: groupedLimits.general.length, - }); + // Filter limits to show only credits and account management (Phase 0: Credit-only system) + const creditLimits = limits.filter(l => l.category === 'credits'); + const accountLimits = limits.filter(l => l.category === 'account'); return (
- +
-

Acoount Limits Usage 12

-

Monitor your plan limits and usage statistics

+

Credit Usage & Limits

+

Monitor your credit usage and account management limits

- {/* Debug Info - Remove in production */} - {import.meta.env.DEV && ( - -
- Debug: Loading={limitsLoading ? 'Yes' : 'No'}, Limits={limits.length}, - Planner={groupedLimits.planner.length}, Writer={groupedLimits.writer.length}, - Images={groupedLimits.images.length}, AI={groupedLimits.ai.length}, General={groupedLimits.general.length} -
-
- )} + {/* Credit Costs Reference */} + +

Credit Costs per Operation

+
+ {Object.entries(CREDIT_COSTS).map(([operation, info]) => ( +
+
+
+ {operation.replace(/_/g, ' ')} +
+
+ {info.description} +
+
+
+ + {typeof info.cost === 'number' ? `${info.cost} credits` : info.cost} + +
+
+ ))} +
+
- {/* Limit Cards by Category */} + {/* Credit Limits */} {limitsLoading ? (
Loading limits...
- ) : limits.length === 0 ? ( - -
-

No usage limits data available.

-

The API endpoint may not be responding or your account may not have a plan configured.

-

Check browser console for errors. Endpoint: /v1/billing/credits/usage/limits/

-
-
) : (
- {/* Planner Limits */} - {groupedLimits.planner.length > 0 && ( + {/* Credit Usage Limits */} + {creditLimits.length > 0 && (
-

Planner Limits

+

Credit Usage

- {groupedLimits.planner.map((limit, idx) => ( + {creditLimits.map((limit, idx) => ( ))}
)} - {/* Writer Limits */} - {groupedLimits.writer.length > 0 && ( + {/* Account Management Limits */} + {accountLimits.length > 0 && (
-

Writer Limits

+

Account Management

- {groupedLimits.writer.map((limit, idx) => ( + {accountLimits.map((limit, idx) => ( ))}
)} - {/* Image Limits */} - {groupedLimits.images.length > 0 && ( -
-

Image Generation Limits

-
- {groupedLimits.images.map((limit, idx) => ( - - ))} + {creditLimits.length === 0 && accountLimits.length === 0 && ( + +
+

No limits data available.

+

Your account may not have a plan configured.

-
- )} - - {/* AI Credits */} - {groupedLimits.ai.length > 0 && ( -
-

AI Credits

-
- {groupedLimits.ai.map((limit, idx) => ( - - ))} -
-
- )} - - {/* General Limits */} - {groupedLimits.general.length > 0 && ( -
-

General Limits

-
- {groupedLimits.general.map((limit, idx) => ( - - ))} -
-
+ )}
)} @@ -219,22 +189,20 @@ export default function Usage() { function LimitCardComponent({ limit }: { limit: LimitCard }) { const getCategoryColor = (category: string) => { switch (category) { - case 'planner': return 'blue'; - case 'writer': return 'green'; - case 'images': return 'purple'; - case 'ai': return 'orange'; - case 'general': return 'gray'; + case 'credits': return 'primary'; + case 'account': return 'gray'; default: return 'gray'; } }; - const getUsageStatus = (percentage: number) => { + const getUsageStatus = (percentage: number | null) => { + if (percentage === null) return 'info'; if (percentage >= 90) return 'danger'; if (percentage >= 75) return 'warning'; return 'success'; }; - const percentage = Math.min(limit.percentage, 100); + const percentage = limit.percentage !== null && limit.percentage !== undefined ? Math.min(limit.percentage, 100) : null; const status = getUsageStatus(percentage); const color = getCategoryColor(limit.category); @@ -242,12 +210,16 @@ function LimitCardComponent({ limit }: { limit: LimitCard }) { ? 'bg-red-500' : status === 'warning' ? 'bg-yellow-500' + : status === 'info' + ? 'bg-blue-500' : 'bg-green-500'; const statusTextColor = status === 'danger' ? 'text-red-600 dark:text-red-400' : status === 'warning' ? 'text-yellow-600 dark:text-yellow-400' + : status === 'info' + ? 'text-blue-600 dark:text-blue-400' : 'text-green-600 dark:text-green-400'; return ( @@ -258,26 +230,44 @@ function LimitCardComponent({ limit }: { limit: LimitCard }) {
- {limit.used.toLocaleString()} - / {limit.limit.toLocaleString()} - {limit.unit} + {limit.limit !== null && limit.limit !== undefined ? ( + <> + {limit.used.toLocaleString()} + / {limit.limit.toLocaleString()} + + ) : ( + + {limit.available !== null && limit.available !== undefined ? limit.available.toLocaleString() : limit.used.toLocaleString()} + + )} + {limit.unit && ( + {limit.unit} + )}
-
-
-
+ {percentage !== null && ( +
+
+
+
-
+ )}
- - {limit.available.toLocaleString()} available - - - {percentage.toFixed(1)}% used - + {limit.available !== null && limit.available !== undefined ? ( + + {limit.available.toLocaleString()} available + + ) : ( + Current value + )} + {percentage !== null && ( + + {percentage.toFixed(1)}% used + + )}
);