@nordsym/apiclaw 1.2.2 → 1.2.3
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 +50 -33
- package/README.md +22 -12
- package/SOUL.md +60 -19
- package/STATUS.md +91 -169
- package/convex/_generated/api.d.ts +6 -0
- package/convex/directCall.ts +598 -0
- package/convex/providers.ts +341 -26
- package/convex/schema.ts +87 -0
- package/convex/usage.ts +260 -0
- package/convex/waitlist.ts +55 -0
- package/data/combined-02-26.json +22102 -0
- package/data/night-expansion-02-26-06-batch2.json +1898 -0
- package/data/night-expansion-02-26-06-batch3.json +1410 -0
- package/data/night-expansion-02-26-06.json +3146 -0
- package/data/night-expansion-02-26-full.json +9726 -0
- package/data/night-expansion-02-26-v2.json +330 -0
- package/data/night-expansion-02-26.json +171 -0
- package/dist/crypto.d.ts +7 -0
- package/dist/crypto.d.ts.map +1 -0
- package/dist/crypto.js +67 -0
- package/dist/crypto.js.map +1 -0
- package/dist/execute-dynamic.d.ts +116 -0
- package/dist/execute-dynamic.d.ts.map +1 -0
- package/dist/execute-dynamic.js +456 -0
- package/dist/execute-dynamic.js.map +1 -0
- package/dist/execute.d.ts +2 -1
- package/dist/execute.d.ts.map +1 -1
- package/dist/execute.js +35 -5
- package/dist/execute.js.map +1 -1
- package/dist/index.js +33 -4
- package/dist/index.js.map +1 -1
- package/dist/registry/apis.json +2081 -3
- package/docs/PRD-customer-key-passthrough.md +184 -0
- package/landing/public/badges/available-on-apiclaw.svg +14 -0
- package/landing/scripts/generate-stats.js +75 -4
- package/landing/src/app/admin/page.tsx +1 -1
- package/landing/src/app/api/auth/magic-link/route.ts +1 -1
- package/landing/src/app/api/auth/session/route.ts +1 -1
- package/landing/src/app/api/auth/verify/route.ts +1 -1
- package/landing/src/app/api/og/route.tsx +5 -3
- package/landing/src/app/docs/page.tsx +5 -4
- package/landing/src/app/earn/page.tsx +14 -11
- package/landing/src/app/globals.css +16 -15
- package/landing/src/app/layout.tsx +2 -2
- package/landing/src/app/page.tsx +425 -254
- package/landing/src/app/providers/dashboard/[apiId]/actions/[actionId]/edit/page.tsx +600 -0
- package/landing/src/app/providers/dashboard/[apiId]/actions/new/page.tsx +583 -0
- package/landing/src/app/providers/dashboard/[apiId]/actions/page.tsx +301 -0
- package/landing/src/app/providers/dashboard/[apiId]/direct-call/page.tsx +659 -0
- package/landing/src/app/providers/dashboard/[apiId]/page.tsx +381 -0
- package/landing/src/app/providers/dashboard/[apiId]/test/page.tsx +418 -0
- package/landing/src/app/providers/dashboard/layout.tsx +292 -0
- package/landing/src/app/providers/dashboard/page.tsx +353 -290
- package/landing/src/app/providers/register/page.tsx +87 -10
- package/landing/src/components/AiClientDropdown.tsx +85 -0
- package/landing/src/components/ConfigHelperModal.tsx +113 -0
- package/landing/src/components/HeroTabs.tsx +187 -0
- package/landing/src/components/ShareIntegrationModal.tsx +198 -0
- package/landing/src/hooks/useDashboardData.ts +53 -1
- package/landing/src/lib/apis.json +46554 -174
- package/landing/src/lib/convex-client.ts +22 -3
- package/landing/src/lib/stats.json +4 -4
- package/landing/tsconfig.tsbuildinfo +1 -1
- package/night-expansion-02-26-06-batch2.py +368 -0
- package/night-expansion-02-26-06-batch3.py +299 -0
- package/night-expansion-02-26-06.py +756 -0
- package/package.json +1 -1
- package/scripts/bulk-add-public-apis-v2.py +418 -0
- package/scripts/night-expansion-02-26-v2.py +296 -0
- package/scripts/night-expansion-02-26.py +890 -0
- package/scripts/seed-complete-api.js +181 -0
- package/scripts/seed-demo-api.sh +44 -0
- package/src/crypto.ts +75 -0
- package/src/execute-dynamic.ts +589 -0
- package/src/execute.ts +41 -5
- package/src/index.ts +38 -4
- package/src/registry/apis.json +2081 -3
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { useState } from "react";
|
|
3
|
+
import { useState, useEffect } from "react";
|
|
4
|
+
import { useSearchParams } from "next/navigation";
|
|
4
5
|
import {
|
|
5
6
|
BarChart3,
|
|
6
7
|
CreditCard,
|
|
@@ -20,6 +21,8 @@ import {
|
|
|
20
21
|
Loader2,
|
|
21
22
|
RefreshCw,
|
|
22
23
|
Plus,
|
|
24
|
+
Rocket,
|
|
25
|
+
X,
|
|
23
26
|
} from "lucide-react";
|
|
24
27
|
import {
|
|
25
28
|
LineChart,
|
|
@@ -39,18 +42,28 @@ import Link from "next/link";
|
|
|
39
42
|
import { useDashboardData } from "@/hooks/useDashboardData";
|
|
40
43
|
import type { ProviderAPI, Analytics, Earnings } from "@/lib/convex-client";
|
|
41
44
|
|
|
42
|
-
type TabType = "overview" | "apis" | "
|
|
45
|
+
type TabType = "overview" | "apis" | "analytics";
|
|
43
46
|
|
|
44
47
|
const COLORS = ["#ef4444", "#f97316", "#eab308", "#22c55e", "#3b82f6"];
|
|
45
48
|
|
|
46
49
|
export default function DashboardPage() {
|
|
47
50
|
const { session, apis, analytics, earnings, isLoading, error, refresh, logout } = useDashboardData();
|
|
48
|
-
const
|
|
51
|
+
const searchParams = useSearchParams();
|
|
52
|
+
const tabFromUrl = searchParams.get("tab") as TabType | null;
|
|
53
|
+
const [activeTab, setActiveTab] = useState<TabType>(tabFromUrl || "overview");
|
|
54
|
+
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
if (tabFromUrl && ["overview", "apis", "analytics"].includes(tabFromUrl)) {
|
|
57
|
+
setActiveTab(tabFromUrl);
|
|
58
|
+
} else if (!tabFromUrl) {
|
|
59
|
+
setActiveTab("overview");
|
|
60
|
+
}
|
|
61
|
+
}, [tabFromUrl]);
|
|
49
62
|
|
|
50
63
|
const tabs = [
|
|
51
64
|
{ id: "overview" as TabType, label: "Overview", icon: BarChart3 },
|
|
52
65
|
{ id: "apis" as TabType, label: "APIs", icon: Zap },
|
|
53
|
-
{ id: "
|
|
66
|
+
{ id: "analytics" as TabType, label: "Analytics", icon: TrendingUp },
|
|
54
67
|
];
|
|
55
68
|
|
|
56
69
|
if (isLoading) {
|
|
@@ -141,7 +154,7 @@ export default function DashboardPage() {
|
|
|
141
154
|
<OverviewTab apis={apis} analytics={analytics} />
|
|
142
155
|
)}
|
|
143
156
|
{activeTab === "apis" && <ApisTab apis={apis} />}
|
|
144
|
-
{activeTab === "
|
|
157
|
+
{activeTab === "analytics" && <UsageTab apis={apis} analytics={analytics} />}
|
|
145
158
|
</div>
|
|
146
159
|
</div>
|
|
147
160
|
);
|
|
@@ -158,201 +171,128 @@ function OverviewTab({
|
|
|
158
171
|
apis: ProviderAPI[];
|
|
159
172
|
analytics: Analytics | null;
|
|
160
173
|
}) {
|
|
161
|
-
const
|
|
174
|
+
const totalCalls = analytics?.totalCalls || 0;
|
|
175
|
+
const totalDiscoveries = apis.reduce((sum, a) => sum + (a.discoveryCount || 0), 0);
|
|
162
176
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
<
|
|
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
|
+
return (
|
|
178
|
+
<div className="space-y-8">
|
|
179
|
+
<h2 className="text-2xl font-bold">Overview</h2>
|
|
180
|
+
|
|
181
|
+
{/* Quick Stats */}
|
|
182
|
+
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
|
|
183
|
+
<div className="rounded-2xl border border-accent/30 bg-accent/10 p-6">
|
|
184
|
+
<div className="flex items-center gap-3 mb-3">
|
|
185
|
+
<Zap className="w-6 h-6 text-accent" />
|
|
186
|
+
<span className="text-text-muted">Listed APIs</span>
|
|
177
187
|
</div>
|
|
178
|
-
<
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
<
|
|
184
|
-
{apis.reduce((sum, a) => sum + (a.discoveryCount || 0), 0)}
|
|
185
|
-
</p>
|
|
188
|
+
<p className="text-4xl font-bold text-accent">{apis.length}</p>
|
|
189
|
+
</div>
|
|
190
|
+
<div className="rounded-2xl border border-border bg-surface-elevated p-6">
|
|
191
|
+
<div className="flex items-center gap-3 mb-3">
|
|
192
|
+
<TrendingUp className="w-6 h-6 text-text-muted" />
|
|
193
|
+
<span className="text-text-muted">Total Calls</span>
|
|
186
194
|
</div>
|
|
187
|
-
<
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
<
|
|
195
|
+
<p className="text-4xl font-bold">{totalCalls.toLocaleString()}</p>
|
|
196
|
+
</div>
|
|
197
|
+
<div className="rounded-2xl border border-border bg-surface-elevated p-6">
|
|
198
|
+
<div className="flex items-center gap-3 mb-3">
|
|
199
|
+
<Users className="w-6 h-6 text-text-muted" />
|
|
200
|
+
<span className="text-text-muted">Discoveries</span>
|
|
193
201
|
</div>
|
|
202
|
+
<p className="text-4xl font-bold">{totalDiscoveries}</p>
|
|
194
203
|
</div>
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
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>
|
|
204
|
+
<div className="rounded-2xl border border-border bg-surface-elevated p-6">
|
|
205
|
+
<div className="flex items-center gap-3 mb-3">
|
|
206
|
+
<Check className="w-6 h-6 text-green-500" />
|
|
207
|
+
<span className="text-text-muted">Status</span>
|
|
208
|
+
</div>
|
|
209
|
+
<p className="text-xl font-bold text-green-500">Active</p>
|
|
216
210
|
</div>
|
|
211
|
+
</div>
|
|
217
212
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
213
|
+
{/* Your APIs */}
|
|
214
|
+
<div>
|
|
215
|
+
<div className="flex items-center justify-between mb-4">
|
|
216
|
+
<h3 className="font-bold text-lg">Your APIs</h3>
|
|
217
|
+
<Link href="/providers/register" className="btn-secondary !py-2 !px-4 text-sm">
|
|
218
|
+
<Plus className="w-4 h-4" />
|
|
219
|
+
Add API
|
|
220
|
+
</Link>
|
|
221
|
+
</div>
|
|
222
|
+
<div className="grid gap-4">
|
|
223
|
+
{apis.map((api) => (
|
|
224
|
+
<Link key={api._id} href={`/providers/dashboard/${api._id}`} className="block rounded-xl border border-border bg-surface-elevated p-5 hover:border-accent/50 transition cursor-pointer">
|
|
225
|
+
<div className="flex items-center justify-between mb-2">
|
|
226
|
+
<h4 className="font-semibold">{api.name}</h4>
|
|
227
|
+
<div className="flex items-center gap-2">
|
|
228
|
+
{api.hasDirectCall && (
|
|
229
|
+
<span className={`px-2 py-0.5 rounded-full text-xs font-medium ${
|
|
230
|
+
api.directCallStatus === "live" ? "bg-cyan-500/20 text-cyan-500" : "bg-purple-500/20 text-purple-500"
|
|
231
|
+
}`}>
|
|
232
|
+
⚡ Direct Call
|
|
233
|
+
</span>
|
|
234
|
+
)}
|
|
232
235
|
<span className={`px-2 py-0.5 rounded-full text-xs font-medium ${
|
|
233
236
|
api.status === "approved" ? "bg-green-500/20 text-green-500" : "bg-yellow-500/20 text-yellow-600"
|
|
234
237
|
}`}>
|
|
235
238
|
{api.status}
|
|
236
239
|
</span>
|
|
237
240
|
</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
241
|
</div>
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
<
|
|
253
|
-
|
|
254
|
-
<
|
|
255
|
-
|
|
256
|
-
|
|
242
|
+
<p className="text-text-muted text-sm line-clamp-2 mb-3">{api.description}</p>
|
|
243
|
+
<div className="flex items-center gap-4 text-sm text-text-muted">
|
|
244
|
+
<span>{api.category}</span>
|
|
245
|
+
<span>{api.discoveryCount || 0} discoveries</span>
|
|
246
|
+
{api.docsUrl && (
|
|
247
|
+
<a href={api.docsUrl} target="_blank" rel="noopener noreferrer" className="text-accent hover:underline flex items-center gap-1" onClick={(e) => e.stopPropagation()}>
|
|
248
|
+
Docs <ExternalLink className="w-3 h-3" />
|
|
249
|
+
</a>
|
|
250
|
+
)}
|
|
257
251
|
</div>
|
|
258
|
-
|
|
259
|
-
|
|
252
|
+
</Link>
|
|
253
|
+
))}
|
|
254
|
+
{apis.length === 0 && (
|
|
255
|
+
<div className="text-center py-12 rounded-xl border border-dashed border-border">
|
|
256
|
+
<p className="text-text-muted mb-4">No APIs listed yet</p>
|
|
257
|
+
<Link href="/providers/register" className="btn-primary">
|
|
258
|
+
<Plus className="w-5 h-5" />
|
|
259
|
+
List Your First API
|
|
260
|
+
</Link>
|
|
261
|
+
</div>
|
|
262
|
+
)}
|
|
260
263
|
</div>
|
|
261
264
|
</div>
|
|
262
|
-
);
|
|
263
|
-
}
|
|
264
265
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
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>
|
|
266
|
+
{/* Quick Actions */}
|
|
267
|
+
<div className="rounded-2xl border border-border bg-surface-elevated p-6">
|
|
268
|
+
<h3 className="font-bold text-lg mb-4">Quick Actions</h3>
|
|
269
|
+
<div className="grid md:grid-cols-3 gap-4">
|
|
270
|
+
<Link href="/providers/register" className="flex items-center gap-3 p-4 rounded-xl border border-border hover:border-accent/50 transition">
|
|
271
|
+
<Plus className="w-8 h-8 text-accent" />
|
|
272
|
+
<div>
|
|
273
|
+
<p className="font-medium">Add API</p>
|
|
274
|
+
<p className="text-sm text-text-muted">List a new API</p>
|
|
329
275
|
</div>
|
|
330
|
-
</
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
<
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
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
|
-
)}
|
|
276
|
+
</Link>
|
|
277
|
+
<button
|
|
278
|
+
onClick={() => window.location.href = '/providers/dashboard?tab=analytics'}
|
|
279
|
+
className="flex items-center gap-3 p-4 rounded-xl border border-border hover:border-accent/50 transition text-left"
|
|
280
|
+
>
|
|
281
|
+
<BarChart3 className="w-8 h-8 text-accent" />
|
|
282
|
+
<div>
|
|
283
|
+
<p className="font-medium">View Usage</p>
|
|
284
|
+
<p className="text-sm text-text-muted">Detailed analytics</p>
|
|
352
285
|
</div>
|
|
353
|
-
</
|
|
286
|
+
</button>
|
|
287
|
+
<a href="https://github.com/nordsym/apiclaw" target="_blank" rel="noopener noreferrer" className="flex items-center gap-3 p-4 rounded-xl border border-border hover:border-accent/50 transition">
|
|
288
|
+
<ExternalLink className="w-8 h-8 text-accent" />
|
|
289
|
+
<div>
|
|
290
|
+
<p className="font-medium">Documentation</p>
|
|
291
|
+
<p className="text-sm text-text-muted">Integration guides</p>
|
|
292
|
+
</div>
|
|
293
|
+
</a>
|
|
354
294
|
</div>
|
|
355
|
-
|
|
295
|
+
</div>
|
|
356
296
|
</div>
|
|
357
297
|
);
|
|
358
298
|
}
|
|
@@ -393,7 +333,42 @@ function StatCard({
|
|
|
393
333
|
// APIS TAB
|
|
394
334
|
// ============================================
|
|
395
335
|
|
|
396
|
-
function ApisTab({ apis }: { apis: ProviderAPI[] }) {
|
|
336
|
+
function ApisTab({ apis, onDelete }: { apis: ProviderAPI[], onDelete?: (apiId: string) => void }) {
|
|
337
|
+
const [deleteConfirm, setDeleteConfirm] = useState<string | null>(null);
|
|
338
|
+
const [deleting, setDeleting] = useState(false);
|
|
339
|
+
|
|
340
|
+
const handleDelete = async (apiId: string, apiName: string) => {
|
|
341
|
+
if (deleteConfirm !== apiId) {
|
|
342
|
+
setDeleteConfirm(apiId);
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
setDeleting(true);
|
|
347
|
+
try {
|
|
348
|
+
const token = localStorage.getItem("apiclaw_session");
|
|
349
|
+
if (!token) return;
|
|
350
|
+
|
|
351
|
+
const response = await fetch(`${process.env.NEXT_PUBLIC_CONVEX_URL || 'https://adventurous-avocet-799.convex.cloud'}/api/mutation`, {
|
|
352
|
+
method: 'POST',
|
|
353
|
+
headers: { 'Content-Type': 'application/json' },
|
|
354
|
+
body: JSON.stringify({
|
|
355
|
+
path: 'providers:deleteAPI',
|
|
356
|
+
args: { token, apiId }
|
|
357
|
+
})
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
if (response.ok) {
|
|
361
|
+
onDelete?.(apiId);
|
|
362
|
+
window.location.reload();
|
|
363
|
+
}
|
|
364
|
+
} catch (error) {
|
|
365
|
+
console.error('Delete failed:', error);
|
|
366
|
+
} finally {
|
|
367
|
+
setDeleting(false);
|
|
368
|
+
setDeleteConfirm(null);
|
|
369
|
+
}
|
|
370
|
+
};
|
|
371
|
+
|
|
397
372
|
return (
|
|
398
373
|
<div className="space-y-6">
|
|
399
374
|
<div className="flex items-center justify-between">
|
|
@@ -417,26 +392,62 @@ function ApisTab({ apis }: { apis: ProviderAPI[] }) {
|
|
|
417
392
|
) : (
|
|
418
393
|
<div className="grid gap-4">
|
|
419
394
|
{apis.map((api) => (
|
|
420
|
-
<div key={api._id} className="rounded-2xl border border-border bg-surface-elevated p-6">
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
395
|
+
<div key={api._id} className="relative rounded-2xl border border-border bg-surface-elevated p-6 hover:border-accent/50 transition">
|
|
396
|
+
{/* Delete button */}
|
|
397
|
+
<button
|
|
398
|
+
onClick={(e) => {
|
|
399
|
+
e.preventDefault();
|
|
400
|
+
e.stopPropagation();
|
|
401
|
+
handleDelete(api._id, api.name);
|
|
402
|
+
}}
|
|
403
|
+
className={`absolute top-4 right-4 p-2 rounded-lg transition ${
|
|
404
|
+
deleteConfirm === api._id
|
|
405
|
+
? 'bg-red-500 text-white'
|
|
406
|
+
: 'hover:bg-red-500/20 text-text-muted hover:text-red-500'
|
|
407
|
+
}`}
|
|
408
|
+
title={deleteConfirm === api._id ? 'Click again to confirm' : 'Delete API'}
|
|
409
|
+
>
|
|
410
|
+
{deleting && deleteConfirm === api._id ? (
|
|
411
|
+
<Loader2 className="w-4 h-4 animate-spin" />
|
|
412
|
+
) : (
|
|
413
|
+
<X className="w-4 h-4" />
|
|
414
|
+
)}
|
|
415
|
+
</button>
|
|
416
|
+
{deleteConfirm === api._id && (
|
|
417
|
+
<div className="absolute top-14 right-4 bg-surface-elevated border border-red-500/50 rounded-lg px-3 py-2 text-sm text-red-500 shadow-lg">
|
|
418
|
+
Click again to delete
|
|
419
|
+
</div>
|
|
420
|
+
)}
|
|
421
|
+
|
|
422
|
+
<Link href={`/providers/dashboard/${api._id}`} className="block cursor-pointer">
|
|
423
|
+
<div className="flex items-center justify-between mb-3 pr-10">
|
|
424
|
+
<div className="flex items-center gap-3">
|
|
425
|
+
<Zap className="w-8 h-8 text-accent" />
|
|
426
|
+
<div>
|
|
427
|
+
<h3 className="font-semibold text-lg">{api.name}</h3>
|
|
428
|
+
<span className="text-sm text-text-muted">{api.category}</span>
|
|
429
|
+
</div>
|
|
430
|
+
</div>
|
|
431
|
+
<div className="flex items-center gap-2">
|
|
432
|
+
{api.hasDirectCall && (
|
|
433
|
+
<span className={`px-3 py-1 rounded-full text-sm font-medium ${
|
|
434
|
+
api.directCallStatus === "live" ? "bg-cyan-500/20 text-cyan-500" : "bg-purple-500/20 text-purple-500"
|
|
435
|
+
}`}>
|
|
436
|
+
⚡ Direct Call
|
|
437
|
+
</span>
|
|
438
|
+
)}
|
|
439
|
+
<span className={`px-3 py-1 rounded-full text-sm font-medium ${
|
|
440
|
+
api.status === "approved"
|
|
441
|
+
? "bg-green-500/20 text-green-500"
|
|
442
|
+
: api.status === "pending"
|
|
443
|
+
? "bg-yellow-500/20 text-yellow-600"
|
|
444
|
+
: "bg-gray-500/20 text-gray-500"
|
|
445
|
+
}`}>
|
|
446
|
+
{api.status}
|
|
447
|
+
</span>
|
|
427
448
|
</div>
|
|
428
449
|
</div>
|
|
429
|
-
<
|
|
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>
|
|
450
|
+
<p className="text-text-secondary mb-4">{api.description}</p>
|
|
440
451
|
<div className="flex items-center gap-6 text-sm">
|
|
441
452
|
<div>
|
|
442
453
|
<span className="text-text-muted">Pricing:</span>{" "}
|
|
@@ -452,11 +463,13 @@ function ApisTab({ apis }: { apis: ProviderAPI[] }) {
|
|
|
452
463
|
target="_blank"
|
|
453
464
|
rel="noopener noreferrer"
|
|
454
465
|
className="text-accent hover:underline flex items-center gap-1"
|
|
466
|
+
onClick={(e) => e.stopPropagation()}
|
|
455
467
|
>
|
|
456
468
|
Documentation <ExternalLink className="w-3 h-3" />
|
|
457
469
|
</a>
|
|
458
470
|
)}
|
|
459
471
|
</div>
|
|
472
|
+
</Link>
|
|
460
473
|
</div>
|
|
461
474
|
))}
|
|
462
475
|
</div>
|
|
@@ -466,121 +479,171 @@ function ApisTab({ apis }: { apis: ProviderAPI[] }) {
|
|
|
466
479
|
}
|
|
467
480
|
|
|
468
481
|
// ============================================
|
|
469
|
-
//
|
|
482
|
+
// ANALYTICS TAB
|
|
470
483
|
// ============================================
|
|
471
484
|
|
|
472
|
-
function
|
|
473
|
-
const
|
|
485
|
+
function UsageTab({ apis, analytics }: { apis: ProviderAPI[]; analytics: Analytics | null }) {
|
|
486
|
+
const totalCalls = analytics?.totalCalls || 0;
|
|
487
|
+
const uniqueAgents = analytics?.uniqueAgents || 0;
|
|
488
|
+
const totalDiscoveries = apis.reduce((sum, a) => sum + (a.discoveryCount || 0), 0);
|
|
489
|
+
const hasChartData = analytics && analytics.callsByDay && analytics.callsByDay.length > 0;
|
|
474
490
|
|
|
475
491
|
return (
|
|
476
492
|
<div className="space-y-8">
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
<
|
|
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>
|
|
493
|
+
{/* Preview Banner */}
|
|
494
|
+
{analytics?.isPreview && (
|
|
495
|
+
<div className="bg-accent/10 border border-accent/30 rounded-xl p-4 flex items-center gap-3">
|
|
496
|
+
<AlertCircle className="w-5 h-5 text-accent flex-shrink-0" />
|
|
497
|
+
<div>
|
|
498
|
+
<p className="font-medium text-accent">Preview Mode</p>
|
|
499
|
+
<p className="text-sm text-text-muted">This is sample data. Real analytics will appear once agents start using your API.</p>
|
|
501
500
|
</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
501
|
</div>
|
|
502
|
+
)}
|
|
503
|
+
|
|
504
|
+
<h2 className="text-2xl font-bold">Analytics</h2>
|
|
505
|
+
|
|
506
|
+
{/* Stats Grid */}
|
|
507
|
+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
|
508
|
+
<StatCard
|
|
509
|
+
title="Total Calls"
|
|
510
|
+
value={totalCalls.toLocaleString()}
|
|
511
|
+
icon={Zap}
|
|
512
|
+
accent
|
|
513
|
+
/>
|
|
514
|
+
<StatCard
|
|
515
|
+
title="Unique Agents"
|
|
516
|
+
value={uniqueAgents.toString()}
|
|
517
|
+
icon={Users}
|
|
518
|
+
/>
|
|
519
|
+
<StatCard
|
|
520
|
+
title="Avg Latency"
|
|
521
|
+
value={`${analytics?.avgLatency || 145}ms`}
|
|
522
|
+
icon={Clock}
|
|
523
|
+
/>
|
|
524
|
+
<StatCard
|
|
525
|
+
title="Success Rate"
|
|
526
|
+
value={`${(analytics?.successRate || 98.2).toFixed(1)}%`}
|
|
527
|
+
icon={Check}
|
|
528
|
+
/>
|
|
505
529
|
</div>
|
|
506
530
|
|
|
507
|
-
{/*
|
|
508
|
-
|
|
509
|
-
<div className="
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
<
|
|
513
|
-
|
|
514
|
-
|
|
531
|
+
{/* Charts */}
|
|
532
|
+
{hasChartData && (
|
|
533
|
+
<div className="grid lg:grid-cols-3 gap-6">
|
|
534
|
+
{/* Line Chart - Calls Over Time */}
|
|
535
|
+
<div className="lg:col-span-2 bg-surface-elevated rounded-2xl border border-border p-6">
|
|
536
|
+
<h3 className="font-semibold mb-4">Calls Over Time</h3>
|
|
537
|
+
<div className="h-80">
|
|
538
|
+
<ResponsiveContainer width="100%" height="100%">
|
|
539
|
+
<LineChart data={analytics!.callsByDay}>
|
|
540
|
+
<CartesianGrid strokeDasharray="3 3" stroke="var(--border)" />
|
|
541
|
+
<XAxis
|
|
542
|
+
dataKey="date"
|
|
543
|
+
tick={{ fontSize: 12, fill: "var(--text-muted)" }}
|
|
544
|
+
tickFormatter={(d) => new Date(d).toLocaleDateString("en-US", { month: "short", day: "numeric" })}
|
|
545
|
+
/>
|
|
546
|
+
<YAxis tick={{ fontSize: 12, fill: "var(--text-muted)" }} />
|
|
547
|
+
<Tooltip
|
|
548
|
+
contentStyle={{
|
|
549
|
+
background: "var(--surface-elevated)",
|
|
550
|
+
border: "1px solid var(--border)",
|
|
551
|
+
borderRadius: "8px",
|
|
552
|
+
}}
|
|
553
|
+
labelFormatter={(d) => new Date(d).toLocaleDateString()}
|
|
554
|
+
/>
|
|
555
|
+
<Line
|
|
556
|
+
type="monotone"
|
|
557
|
+
dataKey="calls"
|
|
558
|
+
stroke="#ef4444"
|
|
559
|
+
strokeWidth={2}
|
|
560
|
+
dot={false}
|
|
561
|
+
activeDot={{ r: 4, fill: "#ef4444" }}
|
|
562
|
+
/>
|
|
563
|
+
</LineChart>
|
|
564
|
+
</ResponsiveContainer>
|
|
565
|
+
</div>
|
|
515
566
|
</div>
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
567
|
+
|
|
568
|
+
{/* Top Agents */}
|
|
569
|
+
<div className="bg-surface-elevated rounded-2xl border border-border p-6">
|
|
570
|
+
<h3 className="font-semibold mb-4">Top Agents</h3>
|
|
571
|
+
<div className="space-y-3">
|
|
572
|
+
{analytics!.topAgents.slice(0, 6).map((agent, i) => (
|
|
573
|
+
<div key={agent.agentId} className="flex items-center justify-between">
|
|
574
|
+
<div className="flex items-center gap-3">
|
|
575
|
+
<span className="w-6 h-6 rounded-full bg-surface flex items-center justify-center text-xs font-medium text-text-muted">
|
|
576
|
+
{i + 1}
|
|
577
|
+
</span>
|
|
578
|
+
<span className="text-sm font-mono truncate max-w-[140px]">
|
|
579
|
+
{agent.agentId.replace("agent_", "")}
|
|
580
|
+
</span>
|
|
581
|
+
</div>
|
|
582
|
+
<span className="text-sm text-text-muted">{agent.calls.toLocaleString()}</span>
|
|
583
|
+
</div>
|
|
584
|
+
))}
|
|
585
|
+
{analytics!.topAgents.length === 0 && (
|
|
586
|
+
<p className="text-text-muted text-sm">No agent activity yet</p>
|
|
587
|
+
)}
|
|
520
588
|
</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
|
-
)}
|
|
589
|
+
</div>
|
|
527
590
|
</div>
|
|
528
|
-
|
|
591
|
+
)}
|
|
529
592
|
|
|
530
|
-
{/*
|
|
531
|
-
{
|
|
532
|
-
<div className="bg-surface-elevated border border-border
|
|
533
|
-
<
|
|
534
|
-
|
|
593
|
+
{/* Top Actions */}
|
|
594
|
+
{analytics?.topActions && analytics.topActions.length > 0 && (
|
|
595
|
+
<div className="bg-surface-elevated rounded-2xl border border-border p-6">
|
|
596
|
+
<h3 className="font-semibold mb-4">Top Actions</h3>
|
|
597
|
+
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
598
|
+
{analytics.topActions.slice(0, 6).map((action, i) => (
|
|
599
|
+
<div key={action.actionName} className="flex items-center justify-between p-3 rounded-lg bg-surface">
|
|
600
|
+
<div className="flex items-center gap-3">
|
|
601
|
+
<span className="w-6 h-6 rounded-full bg-accent/20 text-accent flex items-center justify-center text-xs font-medium">
|
|
602
|
+
{i + 1}
|
|
603
|
+
</span>
|
|
604
|
+
<span className="text-sm font-mono">{action.actionName}</span>
|
|
605
|
+
</div>
|
|
606
|
+
<span className="text-sm text-text-muted">{action.calls.toLocaleString()}</span>
|
|
607
|
+
</div>
|
|
608
|
+
))}
|
|
535
609
|
</div>
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
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>
|
|
610
|
+
</div>
|
|
611
|
+
)}
|
|
612
|
+
|
|
613
|
+
{/* Usage by API */}
|
|
614
|
+
<div className="bg-surface-elevated border border-border rounded-2xl p-6">
|
|
615
|
+
<h3 className="font-semibold text-lg mb-4">Usage by API</h3>
|
|
616
|
+
{apis.length > 0 ? (
|
|
617
|
+
<div className="space-y-4">
|
|
618
|
+
{apis.map((api) => (
|
|
619
|
+
<div key={api._id} className="flex items-center justify-between p-4 rounded-xl bg-surface">
|
|
620
|
+
<div className="flex items-center gap-3">
|
|
621
|
+
<Zap className="w-5 h-5 text-accent" />
|
|
555
622
|
<div>
|
|
556
|
-
<p className="font-medium"
|
|
557
|
-
<p className="text-sm text-text-muted">
|
|
558
|
-
{new Date(payout.periodStart).toLocaleDateString()} -{" "}
|
|
559
|
-
{new Date(payout.periodEnd).toLocaleDateString()}
|
|
560
|
-
</p>
|
|
623
|
+
<p className="font-medium">{api.name}</p>
|
|
624
|
+
<p className="text-sm text-text-muted">{api.category}</p>
|
|
561
625
|
</div>
|
|
562
626
|
</div>
|
|
563
|
-
<
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
}`}>
|
|
570
|
-
{payout.status}
|
|
571
|
-
</span>
|
|
627
|
+
<div className="text-right">
|
|
628
|
+
<p className="font-semibold">{api.discoveryCount || 0} discoveries</p>
|
|
629
|
+
<p className="text-sm text-text-muted">
|
|
630
|
+
{api.status === "approved" ? "Live" : api.status}
|
|
631
|
+
</p>
|
|
632
|
+
</div>
|
|
572
633
|
</div>
|
|
573
634
|
))}
|
|
574
635
|
</div>
|
|
575
|
-
|
|
576
|
-
|
|
636
|
+
) : (
|
|
637
|
+
<p className="text-text-muted text-center py-8">No APIs listed yet</p>
|
|
638
|
+
)}
|
|
639
|
+
</div>
|
|
577
640
|
|
|
578
|
-
{!
|
|
641
|
+
{totalCalls === 0 && !analytics?.isPreview && (
|
|
579
642
|
<div className="rounded-2xl border border-dashed border-border bg-surface/50 p-12 text-center">
|
|
580
|
-
<
|
|
581
|
-
<h3 className="font-semibold text-lg mb-2">No
|
|
643
|
+
<TrendingUp className="w-12 h-12 text-text-muted mx-auto mb-4" />
|
|
644
|
+
<h3 className="font-semibold text-lg mb-2">No Usage Yet</h3>
|
|
582
645
|
<p className="text-text-muted">
|
|
583
|
-
When agents start using your APIs,
|
|
646
|
+
When agents start using your APIs, analytics stats will appear here.
|
|
584
647
|
</p>
|
|
585
648
|
</div>
|
|
586
649
|
)}
|