123
This commit is contained in:
@@ -3,6 +3,7 @@ import { useToast } from '../../components/ui/toast/ToastContainer';
|
|||||||
import { getCreditTransactions, getCreditBalance, CreditTransaction as BillingTransaction, CreditBalance } from '../../services/billing.api';
|
import { getCreditTransactions, getCreditBalance, CreditTransaction as BillingTransaction, CreditBalance } from '../../services/billing.api';
|
||||||
import { Card } from '../../components/ui/card';
|
import { Card } from '../../components/ui/card';
|
||||||
import Badge from '../../components/ui/badge/Badge';
|
import Badge from '../../components/ui/badge/Badge';
|
||||||
|
import { CompactPagination } from '../ui/pagination';
|
||||||
|
|
||||||
// Credit costs per operation (copied from Billing usage page)
|
// Credit costs per operation (copied from Billing usage page)
|
||||||
const CREDIT_COSTS: Record<string, { cost: number | string; description: string }> = {
|
const CREDIT_COSTS: Record<string, { cost: number | string; description: string }> = {
|
||||||
@@ -22,6 +23,8 @@ export default function BillingUsagePanel() {
|
|||||||
const [transactions, setTransactions] = useState<BillingTransaction[]>([]);
|
const [transactions, setTransactions] = useState<BillingTransaction[]>([]);
|
||||||
const [balance, setBalance] = useState<CreditBalance | null>(null);
|
const [balance, setBalance] = useState<CreditBalance | null>(null);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [page, setPage] = useState(1);
|
||||||
|
const [pageSize, setPageSize] = useState(10);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadUsage();
|
loadUsage();
|
||||||
@@ -43,6 +46,9 @@ export default function BillingUsagePanel() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const totalPages = Math.max(1, Math.ceil(transactions.length / pageSize));
|
||||||
|
const paginated = transactions.slice((page - 1) * pageSize, page * pageSize);
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<div className="p-4">
|
<div className="p-4">
|
||||||
@@ -123,7 +129,7 @@ export default function BillingUsagePanel() {
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{transactions.map((txn) => (
|
{paginated.map((txn) => (
|
||||||
<tr key={txn.id} className="border-b border-gray-100 dark:border-gray-800">
|
<tr key={txn.id} className="border-b border-gray-100 dark:border-gray-800">
|
||||||
<td className="py-3 px-4 text-sm text-gray-900 dark:text-white">{new Date(txn.created_at).toLocaleString()}</td>
|
<td className="py-3 px-4 text-sm text-gray-900 dark:text-white">{new Date(txn.created_at).toLocaleString()}</td>
|
||||||
<td className="py-3 px-4">
|
<td className="py-3 px-4">
|
||||||
@@ -144,6 +150,23 @@ export default function BillingUsagePanel() {
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
{transactions.length > 0 && (
|
||||||
|
<div className="mt-4 flex justify-between items-center">
|
||||||
|
<div className="text-sm text-gray-500 dark:text-gray-400">
|
||||||
|
Showing {(page - 1) * pageSize + 1}-{Math.min(page * pageSize, transactions.length)} of {transactions.length}
|
||||||
|
</div>
|
||||||
|
<CompactPagination
|
||||||
|
currentPage={page}
|
||||||
|
totalPages={totalPages}
|
||||||
|
pageSize={pageSize}
|
||||||
|
onPageChange={(p) => setPage(p)}
|
||||||
|
onPageSizeChange={(size) => {
|
||||||
|
setPageSize(size);
|
||||||
|
setPage(1);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -134,9 +134,11 @@ export default function TeamManagementPage() {
|
|||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<Button
|
<Button
|
||||||
variant="primary"
|
variant="primary"
|
||||||
|
tone="brand"
|
||||||
|
size="md"
|
||||||
|
startIcon={<UserPlus className="w-4 h-4" />}
|
||||||
onClick={() => setShowInviteModal(true)}
|
onClick={() => setShowInviteModal(true)}
|
||||||
>
|
>
|
||||||
<UserPlus className="w-4 h-4 mr-2" />
|
|
||||||
Invite Team Member
|
Invite Team Member
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
@@ -215,9 +217,11 @@ export default function TeamManagementPage() {
|
|||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<Button
|
<Button
|
||||||
variant="primary"
|
variant="primary"
|
||||||
|
tone="brand"
|
||||||
|
size="md"
|
||||||
|
startIcon={<UserPlus className="w-4 h-4" />}
|
||||||
onClick={() => setShowInviteModal(true)}
|
onClick={() => setShowInviteModal(true)}
|
||||||
>
|
>
|
||||||
<UserPlus className="w-4 h-4 mr-2" />
|
|
||||||
Send Invitation
|
Send Invitation
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { Card } from '../../components/ui/card';
|
|||||||
import Badge from '../../components/ui/badge/Badge';
|
import Badge from '../../components/ui/badge/Badge';
|
||||||
import BillingUsagePanel from '../../components/billing/BillingUsagePanel';
|
import BillingUsagePanel from '../../components/billing/BillingUsagePanel';
|
||||||
import BillingBalancePanel from '../../components/billing/BillingBalancePanel';
|
import BillingBalancePanel from '../../components/billing/BillingBalancePanel';
|
||||||
|
import Button from '../../components/ui/button/Button';
|
||||||
|
|
||||||
type TabType = 'credits' | 'api' | 'costs';
|
type TabType = 'credits' | 'api' | 'costs';
|
||||||
|
|
||||||
@@ -92,36 +93,20 @@ export default function UsageAnalyticsPage() {
|
|||||||
|
|
||||||
{/* Period Selector */}
|
{/* Period Selector */}
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<button
|
{[7, 30, 90].map((value) => {
|
||||||
onClick={() => setPeriod(7)}
|
const isActive = period === value;
|
||||||
className={`px-4 py-2 rounded-md text-sm font-medium ${
|
return (
|
||||||
period === 7
|
<Button
|
||||||
? 'bg-primary-600 text-white'
|
key={value}
|
||||||
: 'bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300'
|
size="sm"
|
||||||
}`}
|
variant={isActive ? 'primary' : 'secondary'}
|
||||||
>
|
tone={isActive ? 'brand' : 'neutral'}
|
||||||
7 Days
|
onClick={() => setPeriod(value)}
|
||||||
</button>
|
>
|
||||||
<button
|
{value} Days
|
||||||
onClick={() => setPeriod(30)}
|
</Button>
|
||||||
className={`px-4 py-2 rounded-md text-sm font-medium ${
|
);
|
||||||
period === 30
|
})}
|
||||||
? 'bg-primary-600 text-white'
|
|
||||||
: 'bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
30 Days
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={() => setPeriod(90)}
|
|
||||||
className={`px-4 py-2 rounded-md text-sm font-medium ${
|
|
||||||
period === 90
|
|
||||||
? 'bg-primary-600 text-white'
|
|
||||||
: 'bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
90 Days
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user