@nordsym/apiclaw 1.0.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +74 -0
- package/HEARTBEAT.md +4 -0
- package/IDENTITY.md +22 -0
- package/README.md +193 -202
- package/SOUL.md +36 -0
- package/STATUS.md +237 -0
- package/TOOLS.md +36 -0
- package/USER.md +17 -0
- package/{backend/convex → convex}/_generated/api.d.ts +12 -6
- package/convex/analytics.ts +90 -0
- package/convex/credits.ts +211 -0
- package/convex/http.ts +578 -0
- package/convex/providers.ts +516 -0
- package/convex/purchases.ts +183 -0
- package/convex/ratelimit.ts +104 -0
- package/convex/schema.ts +220 -0
- package/convex/telemetry.ts +81 -0
- package/convex.json +3 -0
- package/dist/credentials.d.ts +19 -0
- package/dist/credentials.d.ts.map +1 -0
- package/dist/credentials.js +158 -0
- package/dist/credentials.js.map +1 -0
- package/dist/credits.d.ts +14 -11
- package/dist/credits.d.ts.map +1 -1
- package/dist/credits.js +151 -99
- package/dist/credits.js.map +1 -1
- package/dist/discovery.d.ts +7 -16
- package/dist/discovery.d.ts.map +1 -1
- package/dist/discovery.js +33 -40
- package/dist/discovery.js.map +1 -1
- package/dist/execute.d.ts +19 -0
- package/dist/execute.d.ts.map +1 -0
- package/dist/execute.js +285 -0
- package/dist/execute.js.map +1 -0
- package/dist/index.js +175 -31
- package/dist/index.js.map +1 -1
- package/dist/proxy.d.ts +6 -0
- package/dist/proxy.d.ts.map +1 -0
- package/dist/proxy.js +19 -0
- package/dist/proxy.js.map +1 -0
- package/dist/registry/apis.json +95362 -202
- package/dist/registry/apis_expanded.json +100853 -0
- package/dist/stripe.d.ts +68 -0
- package/dist/stripe.d.ts.map +1 -0
- package/dist/stripe.js +196 -0
- package/dist/stripe.js.map +1 -0
- package/dist/telemetry.d.ts +28 -0
- package/dist/telemetry.d.ts.map +1 -0
- package/dist/telemetry.js +50 -0
- package/dist/telemetry.js.map +1 -0
- package/dist/test.d.ts +3 -2
- package/dist/test.d.ts.map +1 -1
- package/dist/test.js +105 -75
- package/dist/test.js.map +1 -1
- package/dist/types.d.ts +0 -28
- package/dist/types.d.ts.map +1 -1
- package/dist/webhook.d.ts +2 -0
- package/dist/webhook.d.ts.map +1 -0
- package/dist/webhook.js +90 -0
- package/dist/webhook.js.map +1 -0
- package/landing/DESIGN.md +343 -0
- package/landing/package-lock.json +1196 -7
- package/landing/package.json +5 -1
- package/landing/public/android-chrome-192x192.png +0 -0
- package/landing/public/android-chrome-512x512.png +0 -0
- package/landing/public/apple-touch-icon.png +0 -0
- package/landing/public/demo.gif +0 -0
- package/landing/public/demo.mp4 +0 -0
- package/landing/public/favicon-16x16.png +0 -0
- package/landing/public/favicon-32x32.png +0 -0
- package/landing/public/favicon.ico +0 -0
- package/landing/public/favicon.svg +3 -0
- package/landing/public/icon.svg +47 -0
- package/landing/public/logo-mono.svg +37 -0
- package/landing/public/logo-simple.svg +45 -0
- package/landing/public/logo.svg +84 -0
- package/landing/public/og-template.html +184 -0
- package/landing/public/site.webmanifest +31 -0
- package/landing/scripts/generate-assets.js +284 -0
- package/landing/scripts/generate-pngs.js +48 -0
- package/landing/scripts/generate-stats.js +42 -0
- package/landing/src/app/admin/page.tsx +348 -0
- package/landing/src/app/api/auth/magic-link/route.ts +73 -0
- package/landing/src/app/api/auth/session/route.ts +38 -0
- package/landing/src/app/api/auth/verify/route.ts +43 -0
- package/landing/src/app/api/og/route.tsx +84 -0
- package/landing/src/app/globals.css +439 -100
- package/landing/src/app/layout.tsx +37 -7
- package/landing/src/app/page.tsx +627 -552
- package/landing/src/app/providers/dashboard/login/page.tsx +176 -0
- package/landing/src/app/providers/dashboard/page.tsx +589 -0
- package/landing/src/app/providers/dashboard/verify/page.tsx +106 -0
- package/landing/src/app/providers/layout.tsx +14 -0
- package/landing/src/app/providers/page.tsx +402 -0
- package/landing/src/app/providers/register/page.tsx +670 -0
- package/landing/src/components/ProviderDashboard.tsx +794 -0
- package/landing/src/hooks/useDashboardData.ts +99 -0
- package/landing/src/lib/apis.json +116054 -0
- package/landing/src/lib/convex-client.ts +106 -0
- package/landing/src/lib/mock-data.ts +285 -0
- package/landing/src/lib/stats.json +6 -0
- package/landing/tailwind.config.ts +12 -11
- package/landing/tsconfig.tsbuildinfo +1 -0
- package/package.json +21 -20
- package/scripts/SYMBOT-FIX.md +238 -0
- package/scripts/demo-simulation.py +177 -0
- package/scripts/expand-more.py +502 -0
- package/scripts/expand-registry.py +434 -0
- package/scripts/history-sanitizer.ts +272 -0
- package/scripts/mass-scrape.py +1308 -0
- package/scripts/sync-and-deploy.sh +36 -0
- package/src/credentials.ts +177 -0
- package/src/credits.ts +190 -122
- package/src/discovery.ts +45 -58
- package/src/execute.ts +350 -0
- package/src/index.ts +184 -32
- package/src/proxy.ts +24 -0
- package/src/registry/apis.json +95362 -202
- package/src/registry/apis_expanded.json +100853 -0
- package/src/stripe.ts +243 -0
- package/src/telemetry.ts +71 -0
- package/src/test.ts +127 -89
- package/src/types.ts +0 -34
- package/src/webhook.ts +107 -0
- package/.github/ISSUE_TEMPLATE/add-api.yml +0 -123
- package/BRIEFING.md +0 -30
- package/backend/convex/apiKeys.ts +0 -75
- package/backend/convex/purchases.ts +0 -74
- package/backend/convex/schema.ts +0 -45
- package/backend/convex/transactions.ts +0 -57
- package/backend/convex/users.ts +0 -94
- package/backend/package-lock.json +0 -521
- package/backend/package.json +0 -15
- package/dist/registry/parse_apis.py +0 -146
- package/dist/revenuecat.d.ts +0 -61
- package/dist/revenuecat.d.ts.map +0 -1
- package/dist/revenuecat.js +0 -166
- package/dist/revenuecat.js.map +0 -1
- package/dist/webhooks/revenuecat.d.ts +0 -48
- package/dist/webhooks/revenuecat.d.ts.map +0 -1
- package/dist/webhooks/revenuecat.js +0 -119
- package/dist/webhooks/revenuecat.js.map +0 -1
- package/docs/revenuecat-setup.md +0 -89
- package/landing/src/app/api/keys/route.ts +0 -71
- package/landing/src/app/api/log/route.ts +0 -37
- package/landing/src/app/api/stats/route.ts +0 -37
- package/landing/src/app/page.tsx.bak +0 -567
- package/landing/src/components/AddKeyModal.tsx +0 -159
- package/newsletter-template.html +0 -71
- package/outreach/OUTREACH-SYSTEM.md +0 -211
- package/outreach/email-template.html +0 -179
- package/outreach/targets.md +0 -133
- package/src/registry/parse_apis.py +0 -146
- package/src/revenuecat.ts +0 -239
- package/src/webhooks/revenuecat.ts +0 -187
- /package/{backend/convex → convex}/README.md +0 -0
- /package/{backend/convex → convex}/_generated/api.js +0 -0
- /package/{backend/convex → convex}/_generated/dataModel.d.ts +0 -0
- /package/{backend/convex → convex}/_generated/server.d.ts +0 -0
- /package/{backend/convex → convex}/_generated/server.js +0 -0
- /package/{backend/convex → convex}/tsconfig.json +0 -0
|
@@ -0,0 +1,589 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import {
|
|
5
|
+
BarChart3,
|
|
6
|
+
CreditCard,
|
|
7
|
+
Settings,
|
|
8
|
+
TrendingUp,
|
|
9
|
+
Users,
|
|
10
|
+
Zap,
|
|
11
|
+
DollarSign,
|
|
12
|
+
ArrowUpRight,
|
|
13
|
+
ArrowDownRight,
|
|
14
|
+
ExternalLink,
|
|
15
|
+
ChevronRight,
|
|
16
|
+
Check,
|
|
17
|
+
Clock,
|
|
18
|
+
AlertCircle,
|
|
19
|
+
LogOut,
|
|
20
|
+
Loader2,
|
|
21
|
+
RefreshCw,
|
|
22
|
+
Plus,
|
|
23
|
+
} from "lucide-react";
|
|
24
|
+
import {
|
|
25
|
+
LineChart,
|
|
26
|
+
Line,
|
|
27
|
+
BarChart,
|
|
28
|
+
Bar,
|
|
29
|
+
XAxis,
|
|
30
|
+
YAxis,
|
|
31
|
+
CartesianGrid,
|
|
32
|
+
Tooltip,
|
|
33
|
+
ResponsiveContainer,
|
|
34
|
+
PieChart,
|
|
35
|
+
Pie,
|
|
36
|
+
Cell,
|
|
37
|
+
} from "recharts";
|
|
38
|
+
import Link from "next/link";
|
|
39
|
+
import { useDashboardData } from "@/hooks/useDashboardData";
|
|
40
|
+
import type { ProviderAPI, Analytics, Earnings } from "@/lib/convex-client";
|
|
41
|
+
|
|
42
|
+
type TabType = "overview" | "apis" | "earnings";
|
|
43
|
+
|
|
44
|
+
const COLORS = ["#ef4444", "#f97316", "#eab308", "#22c55e", "#3b82f6"];
|
|
45
|
+
|
|
46
|
+
export default function DashboardPage() {
|
|
47
|
+
const { session, apis, analytics, earnings, isLoading, error, refresh, logout } = useDashboardData();
|
|
48
|
+
const [activeTab, setActiveTab] = useState<TabType>("overview");
|
|
49
|
+
|
|
50
|
+
const tabs = [
|
|
51
|
+
{ id: "overview" as TabType, label: "Overview", icon: BarChart3 },
|
|
52
|
+
{ id: "apis" as TabType, label: "APIs", icon: Zap },
|
|
53
|
+
{ id: "earnings" as TabType, label: "Earnings", icon: CreditCard },
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
if (isLoading) {
|
|
57
|
+
return (
|
|
58
|
+
<div className="min-h-screen flex items-center justify-center">
|
|
59
|
+
<div className="text-center">
|
|
60
|
+
<Loader2 className="w-12 h-12 text-accent animate-spin mx-auto mb-4" />
|
|
61
|
+
<p className="text-text-muted">Loading dashboard...</p>
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (error) {
|
|
68
|
+
return (
|
|
69
|
+
<div className="min-h-screen flex items-center justify-center px-6">
|
|
70
|
+
<div className="text-center max-w-md">
|
|
71
|
+
<AlertCircle className="w-16 h-16 text-red-500 mx-auto mb-4" />
|
|
72
|
+
<h1 className="text-2xl font-bold mb-2">Something Went Wrong</h1>
|
|
73
|
+
<p className="text-text-muted mb-6">{error}</p>
|
|
74
|
+
<button onClick={refresh} className="btn-primary">
|
|
75
|
+
<RefreshCw className="w-5 h-5" />
|
|
76
|
+
Try Again
|
|
77
|
+
</button>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (!session) {
|
|
84
|
+
return null; // Will redirect to login via hook
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<div className="min-h-screen bg-background">
|
|
89
|
+
{/* Header */}
|
|
90
|
+
<header className="border-b border-border bg-surface/50 backdrop-blur-xl sticky top-0 z-50">
|
|
91
|
+
<div className="max-w-7xl mx-auto px-6 py-4 flex items-center justify-between">
|
|
92
|
+
<div className="flex items-center gap-4">
|
|
93
|
+
<Link href="/" className="w-10 h-10 rounded-xl bg-accent/20 flex items-center justify-center text-xl">
|
|
94
|
+
🦞
|
|
95
|
+
</Link>
|
|
96
|
+
<div>
|
|
97
|
+
<h1 className="font-bold text-lg">Provider Dashboard</h1>
|
|
98
|
+
<p className="text-sm text-text-muted">{session.name || session.email}</p>
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
101
|
+
<div className="flex items-center gap-3">
|
|
102
|
+
<button
|
|
103
|
+
onClick={refresh}
|
|
104
|
+
className="p-2 rounded-lg hover:bg-surface transition"
|
|
105
|
+
title="Refresh"
|
|
106
|
+
>
|
|
107
|
+
<RefreshCw className="w-5 h-5 text-text-muted" />
|
|
108
|
+
</button>
|
|
109
|
+
<button
|
|
110
|
+
onClick={logout}
|
|
111
|
+
className="p-2 rounded-lg hover:bg-surface transition"
|
|
112
|
+
title="Sign out"
|
|
113
|
+
>
|
|
114
|
+
<LogOut className="w-5 h-5 text-text-muted" />
|
|
115
|
+
</button>
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
</header>
|
|
119
|
+
|
|
120
|
+
<div className="max-w-7xl mx-auto px-6 py-8">
|
|
121
|
+
{/* Tab Navigation */}
|
|
122
|
+
<div className="flex items-center gap-1 p-1 bg-surface rounded-xl w-fit mb-8">
|
|
123
|
+
{tabs.map((tab) => (
|
|
124
|
+
<button
|
|
125
|
+
key={tab.id}
|
|
126
|
+
onClick={() => setActiveTab(tab.id)}
|
|
127
|
+
className={`flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-medium transition-all ${
|
|
128
|
+
activeTab === tab.id
|
|
129
|
+
? "bg-accent text-white"
|
|
130
|
+
: "text-text-muted hover:text-text-primary"
|
|
131
|
+
}`}
|
|
132
|
+
>
|
|
133
|
+
<tab.icon className="w-4 h-4" />
|
|
134
|
+
{tab.label}
|
|
135
|
+
</button>
|
|
136
|
+
))}
|
|
137
|
+
</div>
|
|
138
|
+
|
|
139
|
+
{/* Tab Content */}
|
|
140
|
+
{activeTab === "overview" && (
|
|
141
|
+
<OverviewTab apis={apis} analytics={analytics} />
|
|
142
|
+
)}
|
|
143
|
+
{activeTab === "apis" && <ApisTab apis={apis} />}
|
|
144
|
+
{activeTab === "earnings" && <EarningsTab earnings={earnings} />}
|
|
145
|
+
</div>
|
|
146
|
+
</div>
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// ============================================
|
|
151
|
+
// OVERVIEW TAB
|
|
152
|
+
// ============================================
|
|
153
|
+
|
|
154
|
+
function OverviewTab({
|
|
155
|
+
apis,
|
|
156
|
+
analytics,
|
|
157
|
+
}: {
|
|
158
|
+
apis: ProviderAPI[];
|
|
159
|
+
analytics: Analytics | null;
|
|
160
|
+
}) {
|
|
161
|
+
const hasData = analytics && analytics.totalCalls > 0;
|
|
162
|
+
|
|
163
|
+
// If no analytics yet, show onboarding state
|
|
164
|
+
if (!hasData) {
|
|
165
|
+
return (
|
|
166
|
+
<div className="space-y-8">
|
|
167
|
+
<h2 className="text-2xl font-bold">Welcome to APIClaw!</h2>
|
|
168
|
+
|
|
169
|
+
{/* Quick Stats */}
|
|
170
|
+
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
171
|
+
<div className="rounded-2xl border border-border bg-surface-elevated p-6">
|
|
172
|
+
<div className="flex items-center gap-3 mb-3">
|
|
173
|
+
<Zap className="w-6 h-6 text-accent" />
|
|
174
|
+
<span className="text-text-muted">Listed APIs</span>
|
|
175
|
+
</div>
|
|
176
|
+
<p className="text-4xl font-bold">{apis.length}</p>
|
|
177
|
+
</div>
|
|
178
|
+
<div className="rounded-2xl border border-border bg-surface-elevated p-6">
|
|
179
|
+
<div className="flex items-center gap-3 mb-3">
|
|
180
|
+
<Users className="w-6 h-6 text-text-muted" />
|
|
181
|
+
<span className="text-text-muted">Agent Discoveries</span>
|
|
182
|
+
</div>
|
|
183
|
+
<p className="text-4xl font-bold">
|
|
184
|
+
{apis.reduce((sum, a) => sum + (a.discoveryCount || 0), 0)}
|
|
185
|
+
</p>
|
|
186
|
+
</div>
|
|
187
|
+
<div className="rounded-2xl border border-border bg-surface-elevated p-6">
|
|
188
|
+
<div className="flex items-center gap-3 mb-3">
|
|
189
|
+
<TrendingUp className="w-6 h-6 text-text-muted" />
|
|
190
|
+
<span className="text-text-muted">Status</span>
|
|
191
|
+
</div>
|
|
192
|
+
<p className="text-xl font-bold text-green-500">Active</p>
|
|
193
|
+
</div>
|
|
194
|
+
</div>
|
|
195
|
+
|
|
196
|
+
{/* Getting Started */}
|
|
197
|
+
<div className="rounded-2xl border border-accent/30 bg-accent/5 p-8">
|
|
198
|
+
<h3 className="font-bold text-xl mb-4">🚀 Getting Started</h3>
|
|
199
|
+
<p className="text-text-secondary mb-6">
|
|
200
|
+
Your APIs are listed and discoverable by AI agents. Here's what happens next:
|
|
201
|
+
</p>
|
|
202
|
+
<ul className="space-y-3 text-text-secondary">
|
|
203
|
+
<li className="flex items-start gap-3">
|
|
204
|
+
<Check className="w-5 h-5 text-green-500 flex-shrink-0 mt-0.5" />
|
|
205
|
+
<span>AI agents can now find your APIs in the APIClaw registry</span>
|
|
206
|
+
</li>
|
|
207
|
+
<li className="flex items-start gap-3">
|
|
208
|
+
<Check className="w-5 h-5 text-green-500 flex-shrink-0 mt-0.5" />
|
|
209
|
+
<span>When agents use your APIs, usage will appear here</span>
|
|
210
|
+
</li>
|
|
211
|
+
<li className="flex items-start gap-3">
|
|
212
|
+
<Clock className="w-5 h-5 text-text-muted flex-shrink-0 mt-0.5" />
|
|
213
|
+
<span>Set up Stripe Connect to receive payouts (coming soon)</span>
|
|
214
|
+
</li>
|
|
215
|
+
</ul>
|
|
216
|
+
</div>
|
|
217
|
+
|
|
218
|
+
{/* Your APIs */}
|
|
219
|
+
<div>
|
|
220
|
+
<div className="flex items-center justify-between mb-4">
|
|
221
|
+
<h3 className="font-bold text-lg">Your APIs</h3>
|
|
222
|
+
<Link href="/providers/register" className="btn-secondary !py-2 !px-4 text-sm">
|
|
223
|
+
<Plus className="w-4 h-4" />
|
|
224
|
+
Add Another
|
|
225
|
+
</Link>
|
|
226
|
+
</div>
|
|
227
|
+
<div className="grid gap-4">
|
|
228
|
+
{apis.map((api) => (
|
|
229
|
+
<div key={api._id} className="rounded-xl border border-border bg-surface-elevated p-5">
|
|
230
|
+
<div className="flex items-center justify-between mb-2">
|
|
231
|
+
<h4 className="font-semibold">{api.name}</h4>
|
|
232
|
+
<span className={`px-2 py-0.5 rounded-full text-xs font-medium ${
|
|
233
|
+
api.status === "approved" ? "bg-green-500/20 text-green-500" : "bg-yellow-500/20 text-yellow-600"
|
|
234
|
+
}`}>
|
|
235
|
+
{api.status}
|
|
236
|
+
</span>
|
|
237
|
+
</div>
|
|
238
|
+
<p className="text-text-muted text-sm line-clamp-2 mb-3">{api.description}</p>
|
|
239
|
+
<div className="flex items-center gap-4 text-sm text-text-muted">
|
|
240
|
+
<span>{api.category}</span>
|
|
241
|
+
<span>{api.discoveryCount || 0} discoveries</span>
|
|
242
|
+
{api.docsUrl && (
|
|
243
|
+
<a href={api.docsUrl} target="_blank" rel="noopener noreferrer" className="text-accent hover:underline flex items-center gap-1">
|
|
244
|
+
Docs <ExternalLink className="w-3 h-3" />
|
|
245
|
+
</a>
|
|
246
|
+
)}
|
|
247
|
+
</div>
|
|
248
|
+
</div>
|
|
249
|
+
))}
|
|
250
|
+
{apis.length === 0 && (
|
|
251
|
+
<div className="text-center py-12 rounded-xl border border-dashed border-border">
|
|
252
|
+
<p className="text-text-muted mb-4">No APIs listed yet</p>
|
|
253
|
+
<Link href="/providers/register" className="btn-primary">
|
|
254
|
+
<Plus className="w-5 h-5" />
|
|
255
|
+
List Your First API
|
|
256
|
+
</Link>
|
|
257
|
+
</div>
|
|
258
|
+
)}
|
|
259
|
+
</div>
|
|
260
|
+
</div>
|
|
261
|
+
</div>
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Has analytics data - show charts
|
|
266
|
+
return (
|
|
267
|
+
<div className="space-y-8">
|
|
268
|
+
<h2 className="text-2xl font-bold">Analytics</h2>
|
|
269
|
+
|
|
270
|
+
{/* Stats Grid */}
|
|
271
|
+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
|
272
|
+
<StatCard
|
|
273
|
+
title="Total Calls"
|
|
274
|
+
value={analytics.totalCalls.toLocaleString()}
|
|
275
|
+
icon={Zap}
|
|
276
|
+
/>
|
|
277
|
+
<StatCard
|
|
278
|
+
title="Unique Agents"
|
|
279
|
+
value={analytics.uniqueAgents.toString()}
|
|
280
|
+
icon={Users}
|
|
281
|
+
/>
|
|
282
|
+
<StatCard
|
|
283
|
+
title="Revenue"
|
|
284
|
+
value={`$${analytics.totalRevenue.toFixed(2)}`}
|
|
285
|
+
icon={DollarSign}
|
|
286
|
+
accent
|
|
287
|
+
/>
|
|
288
|
+
<StatCard
|
|
289
|
+
title="Listed APIs"
|
|
290
|
+
value={apis.length.toString()}
|
|
291
|
+
icon={TrendingUp}
|
|
292
|
+
/>
|
|
293
|
+
</div>
|
|
294
|
+
|
|
295
|
+
{/* Charts */}
|
|
296
|
+
{analytics.callsByDay.length > 0 && (
|
|
297
|
+
<div className="grid lg:grid-cols-3 gap-6">
|
|
298
|
+
{/* Line Chart - Calls Over Time */}
|
|
299
|
+
<div className="lg:col-span-2 bg-surface-elevated rounded-2xl border border-border p-6">
|
|
300
|
+
<h3 className="font-semibold mb-4">Calls Over Time</h3>
|
|
301
|
+
<div className="h-80">
|
|
302
|
+
<ResponsiveContainer width="100%" height="100%">
|
|
303
|
+
<LineChart data={analytics.callsByDay}>
|
|
304
|
+
<CartesianGrid strokeDasharray="3 3" stroke="var(--border)" />
|
|
305
|
+
<XAxis
|
|
306
|
+
dataKey="date"
|
|
307
|
+
tick={{ fontSize: 12, fill: "var(--text-muted)" }}
|
|
308
|
+
tickFormatter={(d) => new Date(d).toLocaleDateString("en-US", { month: "short", day: "numeric" })}
|
|
309
|
+
/>
|
|
310
|
+
<YAxis tick={{ fontSize: 12, fill: "var(--text-muted)" }} />
|
|
311
|
+
<Tooltip
|
|
312
|
+
contentStyle={{
|
|
313
|
+
background: "var(--surface-elevated)",
|
|
314
|
+
border: "1px solid var(--border)",
|
|
315
|
+
borderRadius: "8px",
|
|
316
|
+
}}
|
|
317
|
+
labelFormatter={(d) => new Date(d).toLocaleDateString()}
|
|
318
|
+
/>
|
|
319
|
+
<Line
|
|
320
|
+
type="monotone"
|
|
321
|
+
dataKey="calls"
|
|
322
|
+
stroke="#ef4444"
|
|
323
|
+
strokeWidth={2}
|
|
324
|
+
dot={false}
|
|
325
|
+
activeDot={{ r: 4, fill: "#ef4444" }}
|
|
326
|
+
/>
|
|
327
|
+
</LineChart>
|
|
328
|
+
</ResponsiveContainer>
|
|
329
|
+
</div>
|
|
330
|
+
</div>
|
|
331
|
+
|
|
332
|
+
{/* Top Agents */}
|
|
333
|
+
<div className="bg-surface-elevated rounded-2xl border border-border p-6">
|
|
334
|
+
<h3 className="font-semibold mb-4">Top Agents</h3>
|
|
335
|
+
<div className="space-y-3">
|
|
336
|
+
{analytics.topAgents.slice(0, 6).map((agent, i) => (
|
|
337
|
+
<div key={agent.agentId} className="flex items-center justify-between">
|
|
338
|
+
<div className="flex items-center gap-3">
|
|
339
|
+
<span className="w-6 h-6 rounded-full bg-surface flex items-center justify-center text-xs font-medium text-text-muted">
|
|
340
|
+
{i + 1}
|
|
341
|
+
</span>
|
|
342
|
+
<span className="text-sm font-mono truncate max-w-[140px]">
|
|
343
|
+
{agent.agentId.replace("agent_", "")}
|
|
344
|
+
</span>
|
|
345
|
+
</div>
|
|
346
|
+
<span className="text-sm text-text-muted">{agent.calls.toLocaleString()}</span>
|
|
347
|
+
</div>
|
|
348
|
+
))}
|
|
349
|
+
{analytics.topAgents.length === 0 && (
|
|
350
|
+
<p className="text-text-muted text-sm">No agent activity yet</p>
|
|
351
|
+
)}
|
|
352
|
+
</div>
|
|
353
|
+
</div>
|
|
354
|
+
</div>
|
|
355
|
+
)}
|
|
356
|
+
</div>
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function StatCard({
|
|
361
|
+
title,
|
|
362
|
+
value,
|
|
363
|
+
change,
|
|
364
|
+
icon: Icon,
|
|
365
|
+
accent,
|
|
366
|
+
}: {
|
|
367
|
+
title: string;
|
|
368
|
+
value: string;
|
|
369
|
+
change?: number;
|
|
370
|
+
icon: typeof Zap;
|
|
371
|
+
accent?: boolean;
|
|
372
|
+
}) {
|
|
373
|
+
return (
|
|
374
|
+
<div className={`rounded-2xl border p-5 ${accent ? "bg-accent/10 border-accent/30" : "bg-surface-elevated border-border"}`}>
|
|
375
|
+
<div className="flex items-center justify-between mb-3">
|
|
376
|
+
<span className="text-sm text-text-muted">{title}</span>
|
|
377
|
+
<Icon className={`w-5 h-5 ${accent ? "text-accent" : "text-text-muted"}`} />
|
|
378
|
+
</div>
|
|
379
|
+
<div className="flex items-end justify-between">
|
|
380
|
+
<span className={`text-3xl font-bold ${accent ? "text-accent" : ""}`}>{value}</span>
|
|
381
|
+
{change !== undefined && (
|
|
382
|
+
<div className={`flex items-center gap-1 text-sm ${change >= 0 ? "text-green-500" : "text-red-500"}`}>
|
|
383
|
+
{change >= 0 ? <ArrowUpRight className="w-4 h-4" /> : <ArrowDownRight className="w-4 h-4" />}
|
|
384
|
+
{Math.abs(change).toFixed(1)}%
|
|
385
|
+
</div>
|
|
386
|
+
)}
|
|
387
|
+
</div>
|
|
388
|
+
</div>
|
|
389
|
+
);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// ============================================
|
|
393
|
+
// APIS TAB
|
|
394
|
+
// ============================================
|
|
395
|
+
|
|
396
|
+
function ApisTab({ apis }: { apis: ProviderAPI[] }) {
|
|
397
|
+
return (
|
|
398
|
+
<div className="space-y-6">
|
|
399
|
+
<div className="flex items-center justify-between">
|
|
400
|
+
<h2 className="text-2xl font-bold">Your APIs</h2>
|
|
401
|
+
<Link href="/providers/register" className="btn-primary !py-2 !px-4 text-sm">
|
|
402
|
+
<Plus className="w-4 h-4" />
|
|
403
|
+
Add API
|
|
404
|
+
</Link>
|
|
405
|
+
</div>
|
|
406
|
+
|
|
407
|
+
{apis.length === 0 ? (
|
|
408
|
+
<div className="text-center py-16 rounded-2xl border border-dashed border-border bg-surface/50">
|
|
409
|
+
<Zap className="w-12 h-12 text-text-muted mx-auto mb-4" />
|
|
410
|
+
<h3 className="font-semibold text-lg mb-2">No APIs Listed</h3>
|
|
411
|
+
<p className="text-text-muted mb-6">List your first API to make it discoverable by AI agents.</p>
|
|
412
|
+
<Link href="/providers/register" className="btn-primary">
|
|
413
|
+
<Plus className="w-5 h-5" />
|
|
414
|
+
List Your First API
|
|
415
|
+
</Link>
|
|
416
|
+
</div>
|
|
417
|
+
) : (
|
|
418
|
+
<div className="grid gap-4">
|
|
419
|
+
{apis.map((api) => (
|
|
420
|
+
<div key={api._id} className="rounded-2xl border border-border bg-surface-elevated p-6">
|
|
421
|
+
<div className="flex items-center justify-between mb-3">
|
|
422
|
+
<div className="flex items-center gap-3">
|
|
423
|
+
<span className="text-3xl">🔌</span>
|
|
424
|
+
<div>
|
|
425
|
+
<h3 className="font-semibold text-lg">{api.name}</h3>
|
|
426
|
+
<span className="text-sm text-text-muted">{api.category}</span>
|
|
427
|
+
</div>
|
|
428
|
+
</div>
|
|
429
|
+
<span className={`px-3 py-1 rounded-full text-sm font-medium ${
|
|
430
|
+
api.status === "approved"
|
|
431
|
+
? "bg-green-500/20 text-green-500"
|
|
432
|
+
: api.status === "pending"
|
|
433
|
+
? "bg-yellow-500/20 text-yellow-600"
|
|
434
|
+
: "bg-gray-500/20 text-gray-500"
|
|
435
|
+
}`}>
|
|
436
|
+
{api.status}
|
|
437
|
+
</span>
|
|
438
|
+
</div>
|
|
439
|
+
<p className="text-text-secondary mb-4">{api.description}</p>
|
|
440
|
+
<div className="flex items-center gap-6 text-sm">
|
|
441
|
+
<div>
|
|
442
|
+
<span className="text-text-muted">Pricing:</span>{" "}
|
|
443
|
+
<span className="capitalize">{api.pricingModel}</span>
|
|
444
|
+
</div>
|
|
445
|
+
<div>
|
|
446
|
+
<span className="text-text-muted">Discoveries:</span>{" "}
|
|
447
|
+
<span>{api.discoveryCount || 0}</span>
|
|
448
|
+
</div>
|
|
449
|
+
{api.docsUrl && (
|
|
450
|
+
<a
|
|
451
|
+
href={api.docsUrl}
|
|
452
|
+
target="_blank"
|
|
453
|
+
rel="noopener noreferrer"
|
|
454
|
+
className="text-accent hover:underline flex items-center gap-1"
|
|
455
|
+
>
|
|
456
|
+
Documentation <ExternalLink className="w-3 h-3" />
|
|
457
|
+
</a>
|
|
458
|
+
)}
|
|
459
|
+
</div>
|
|
460
|
+
</div>
|
|
461
|
+
))}
|
|
462
|
+
</div>
|
|
463
|
+
)}
|
|
464
|
+
</div>
|
|
465
|
+
);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// ============================================
|
|
469
|
+
// EARNINGS TAB
|
|
470
|
+
// ============================================
|
|
471
|
+
|
|
472
|
+
function EarningsTab({ earnings }: { earnings: Earnings | null }) {
|
|
473
|
+
const hasEarnings = earnings && (earnings.totalEarned > 0 || earnings.payouts.length > 0);
|
|
474
|
+
|
|
475
|
+
return (
|
|
476
|
+
<div className="space-y-8">
|
|
477
|
+
<h2 className="text-2xl font-bold">Earnings</h2>
|
|
478
|
+
|
|
479
|
+
{/* Earnings Stats */}
|
|
480
|
+
<div className="grid md:grid-cols-3 gap-4">
|
|
481
|
+
<div className="bg-accent/10 border border-accent/30 rounded-2xl p-6">
|
|
482
|
+
<div className="flex items-center gap-2 mb-3">
|
|
483
|
+
<Clock className="w-5 h-5 text-accent" />
|
|
484
|
+
<span className="text-sm text-text-muted">Pending Payout</span>
|
|
485
|
+
</div>
|
|
486
|
+
<p className="text-4xl font-bold text-accent">${(earnings?.pendingAmount || 0).toFixed(2)}</p>
|
|
487
|
+
<p className="text-sm text-text-muted mt-2">Available for payout</p>
|
|
488
|
+
</div>
|
|
489
|
+
<div className="bg-surface-elevated border border-border rounded-2xl p-6">
|
|
490
|
+
<div className="flex items-center gap-2 mb-3">
|
|
491
|
+
<DollarSign className="w-5 h-5 text-text-muted" />
|
|
492
|
+
<span className="text-sm text-text-muted">Total Earned</span>
|
|
493
|
+
</div>
|
|
494
|
+
<p className="text-4xl font-bold">${(earnings?.totalEarned || 0).toFixed(2)}</p>
|
|
495
|
+
<p className="text-sm text-text-muted mt-2">All time</p>
|
|
496
|
+
</div>
|
|
497
|
+
<div className="bg-surface-elevated border border-border rounded-2xl p-6">
|
|
498
|
+
<div className="flex items-center gap-2 mb-3">
|
|
499
|
+
<Check className="w-5 h-5 text-green-500" />
|
|
500
|
+
<span className="text-sm text-text-muted">Total Paid Out</span>
|
|
501
|
+
</div>
|
|
502
|
+
<p className="text-4xl font-bold">${(earnings?.totalPaidOut || 0).toFixed(2)}</p>
|
|
503
|
+
<p className="text-sm text-text-muted mt-2">Successfully transferred</p>
|
|
504
|
+
</div>
|
|
505
|
+
</div>
|
|
506
|
+
|
|
507
|
+
{/* Stripe Connect */}
|
|
508
|
+
<div className="bg-surface-elevated border border-border rounded-2xl p-6">
|
|
509
|
+
<div className="flex items-center justify-between">
|
|
510
|
+
<div>
|
|
511
|
+
<h3 className="font-semibold text-lg mb-1">Payout Settings</h3>
|
|
512
|
+
<p className="text-sm text-text-muted">
|
|
513
|
+
Connect your Stripe account to receive payouts
|
|
514
|
+
</p>
|
|
515
|
+
</div>
|
|
516
|
+
{earnings?.stripeOnboardingComplete ? (
|
|
517
|
+
<div className="flex items-center gap-2 text-green-500">
|
|
518
|
+
<Check className="w-5 h-5" />
|
|
519
|
+
<span className="font-medium">Connected</span>
|
|
520
|
+
</div>
|
|
521
|
+
) : (
|
|
522
|
+
<button className="btn-primary !py-2 !px-4" disabled>
|
|
523
|
+
<CreditCard className="w-4 h-4" />
|
|
524
|
+
Coming Soon
|
|
525
|
+
</button>
|
|
526
|
+
)}
|
|
527
|
+
</div>
|
|
528
|
+
</div>
|
|
529
|
+
|
|
530
|
+
{/* Payout History */}
|
|
531
|
+
{hasEarnings && earnings.payouts.length > 0 && (
|
|
532
|
+
<div className="bg-surface-elevated border border-border rounded-2xl overflow-hidden">
|
|
533
|
+
<div className="p-6 border-b border-border">
|
|
534
|
+
<h3 className="font-semibold text-lg">Payout History</h3>
|
|
535
|
+
</div>
|
|
536
|
+
<div className="divide-y divide-border">
|
|
537
|
+
{earnings.payouts.map((payout) => (
|
|
538
|
+
<div key={payout.id} className="p-4 flex items-center justify-between">
|
|
539
|
+
<div className="flex items-center gap-4">
|
|
540
|
+
<div className={`w-10 h-10 rounded-full flex items-center justify-center ${
|
|
541
|
+
payout.status === "completed"
|
|
542
|
+
? "bg-green-500/20"
|
|
543
|
+
: payout.status === "processing"
|
|
544
|
+
? "bg-yellow-500/20"
|
|
545
|
+
: "bg-blue-500/20"
|
|
546
|
+
}`}>
|
|
547
|
+
{payout.status === "completed" ? (
|
|
548
|
+
<Check className="w-5 h-5 text-green-500" />
|
|
549
|
+
) : payout.status === "processing" ? (
|
|
550
|
+
<RefreshCw className="w-5 h-5 text-yellow-600 animate-spin" />
|
|
551
|
+
) : (
|
|
552
|
+
<Clock className="w-5 h-5 text-blue-500" />
|
|
553
|
+
)}
|
|
554
|
+
</div>
|
|
555
|
+
<div>
|
|
556
|
+
<p className="font-medium">${payout.amount.toFixed(2)}</p>
|
|
557
|
+
<p className="text-sm text-text-muted">
|
|
558
|
+
{new Date(payout.periodStart).toLocaleDateString()} -{" "}
|
|
559
|
+
{new Date(payout.periodEnd).toLocaleDateString()}
|
|
560
|
+
</p>
|
|
561
|
+
</div>
|
|
562
|
+
</div>
|
|
563
|
+
<span className={`px-2 py-1 rounded-full text-xs font-medium capitalize ${
|
|
564
|
+
payout.status === "completed"
|
|
565
|
+
? "bg-green-500/20 text-green-500"
|
|
566
|
+
: payout.status === "processing"
|
|
567
|
+
? "bg-yellow-500/20 text-yellow-600"
|
|
568
|
+
: "bg-blue-500/20 text-blue-500"
|
|
569
|
+
}`}>
|
|
570
|
+
{payout.status}
|
|
571
|
+
</span>
|
|
572
|
+
</div>
|
|
573
|
+
))}
|
|
574
|
+
</div>
|
|
575
|
+
</div>
|
|
576
|
+
)}
|
|
577
|
+
|
|
578
|
+
{!hasEarnings && (
|
|
579
|
+
<div className="rounded-2xl border border-dashed border-border bg-surface/50 p-12 text-center">
|
|
580
|
+
<DollarSign className="w-12 h-12 text-text-muted mx-auto mb-4" />
|
|
581
|
+
<h3 className="font-semibold text-lg mb-2">No Earnings Yet</h3>
|
|
582
|
+
<p className="text-text-muted">
|
|
583
|
+
When agents start using your APIs, earnings will appear here.
|
|
584
|
+
</p>
|
|
585
|
+
</div>
|
|
586
|
+
)}
|
|
587
|
+
</div>
|
|
588
|
+
);
|
|
589
|
+
}
|