Files
9router/src/app/dashboard/settings/pricing/page.js
T

173 lines
5.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import { useState, useEffect } from "react";
import { useRouter } from "next/navigation";
import Card from "@/shared/components/Card";
import PricingModal from "@/shared/components/PricingModal";
export default function PricingSettingsPage() {
const router = useRouter();
const [showModal, setShowModal] = useState(false);
const [currentPricing, setCurrentPricing] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
loadPricing();
}, []);
const loadPricing = async () => {
setLoading(true);
try {
const response = await fetch("/api/pricing");
if (response.ok) {
const data = await response.json();
setCurrentPricing(data);
}
} catch (error) {
console.error("Failed to load pricing:", error);
} finally {
setLoading(false);
}
};
const handlePricingUpdated = () => {
loadPricing();
};
// Count total models with pricing
const getModelCount = () => {
if (!currentPricing) return 0;
let count = 0;
for (const provider in currentPricing) {
count += Object.keys(currentPricing[provider]).length;
}
return count;
};
// Get providers list
const getProviders = () => {
if (!currentPricing) return [];
return Object.keys(currentPricing).sort();
};
return (
<div className="max-w-6xl mx-auto p-6 space-y-6">
{/* Header */}
<div className="flex items-center justify-between">
<div>
<h1 className="text-3xl font-bold">Pricing Settings</h1>
<p className="text-text-muted mt-1">
Configure pricing rates for cost tracking and calculations
</p>
</div>
<button
onClick={() => setShowModal(true)}
className="px-4 py-2 bg-primary text-white rounded hover:bg-primary/90 transition-colors"
>
Edit Pricing
</button>
</div>
{/* Quick Stats */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<Card className="p-4">
<div className="text-text-muted text-sm uppercase font-semibold">
Total Models
</div>
<div className="text-2xl font-bold mt-1">
{loading ? "..." : getModelCount()}
</div>
</Card>
<Card className="p-4">
<div className="text-text-muted text-sm uppercase font-semibold">
Providers
</div>
<div className="text-2xl font-bold mt-1">
{loading ? "..." : getProviders().length}
</div>
</Card>
<Card className="p-4">
<div className="text-text-muted text-sm uppercase font-semibold">
Status
</div>
<div className="text-2xl font-bold mt-1 text-success">
{loading ? "..." : "Active"}
</div>
</Card>
</div>
{/* Info Section */}
<Card className="p-6">
<h2 className="text-xl font-semibold mb-4">How Pricing Works</h2>
<div className="space-y-3 text-sm text-text-muted">
<p>
<strong>Cost Calculation:</strong> Costs are calculated based on token usage and pricing rates.
Each request&apos;s cost is determined by: (input_tokens × input_rate) + (output_tokens × output_rate) + (cached_tokens × cached_rate)
</p>
<p>
<strong>Pricing Format:</strong> All rates are in <strong>dollars per million tokens</strong> ($/1M tokens).
Example: An input rate of 2.50 means $2.50 per 1,000,000 input tokens.
</p>
<p>
<strong>Token Types:</strong>
</p>
<ul className="list-disc list-inside ml-4 space-y-1">
<li><strong>Input:</strong> Standard prompt tokens</li>
<li><strong>Output:</strong> Completion/response tokens</li>
<li><strong>Cached:</strong> Cached input tokens (typically 50% of input rate)</li>
<li><strong>Reasoning:</strong> Special reasoning/thinking tokens (fallback to output rate)</li>
<li><strong>Cache Creation:</strong> Tokens used to create cache entries (fallback to input rate)</li>
</ul>
<p>
<strong>Custom Pricing:</strong> You can override default pricing for specific models.
Reset to defaults anytime to restore standard rates.
</p>
</div>
</Card>
{/* Current Pricing Preview */}
<Card className="p-6">
<div className="flex items-center justify-between mb-4">
<h2 className="text-xl font-semibold">Current Pricing Overview</h2>
<button
onClick={() => setShowModal(true)}
className="text-primary hover:underline text-sm"
>
View Full Details
</button>
</div>
{loading ? (
<div className="text-center py-4 text-text-muted">Loading pricing data...</div>
) : currentPricing ? (
<div className="space-y-3">
{Object.keys(currentPricing).slice(0, 5).map(provider => (
<div key={provider} className="text-sm">
<span className="font-semibold">{provider.toUpperCase()}:</span>{" "}
<span className="text-text-muted">
{Object.keys(currentPricing[provider]).length} models
</span>
</div>
))}
{Object.keys(currentPricing).length > 5 && (
<div className="text-sm text-text-muted">
+ {Object.keys(currentPricing).length - 5} more providers
</div>
)}
</div>
) : (
<div className="text-text-muted">No pricing data available</div>
)}
</Card>
{/* Pricing Modal */}
{showModal && (
<PricingModal
isOpen={showModal}
onClose={() => setShowModal(false)}
onSave={handlePricingUpdated}
/>
)}
</div>
);
}