hts/apps/migrant/app/[locale]/manage/staffs/[apiId]/page.tsx

300 lines
8.7 KiB
TypeScript

"use client";
import { AreaChart, StackedColumnChart } from "@/components/dashboard/charts";
import { EmptyPlaceholder } from "@/components/dashboard/empty-placeholder";
import { Card, CardContent, CardHeader } from "@/components/ui/card";
import { Separator } from "@/components/ui/separator";
// import { getTenantId } from "@/lib/auth";
// import { and, db, eq, isNull, schema, sql } from "@/lib/db";
import { formatNumber } from "@/lib/fmt";
// import {
// getActiveKeys,
// getActiveKeysDaily,
// getActiveKeysHourly,
// getVerificationsDaily,
// getVerificationsHourly,
// } from "@/lib/tinybird";
import { BarChart } from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { redirect } from "next/navigation";
import { type Interval, IntervalSelect } from "./select";
import PepositoryPage from "../../repository/page";
import { StaffsInfoWithoutId, queryStaffList } from "@/lib/http/staff";
import { useEffect, useState } from "react";
import { PageHeader } from "@/components/dashboard/page-header";
import { CopyButton } from "@/components/dashboard/copy-button";
import UserList from "./item";
export const dynamic = "force-dynamic";
export const runtime = "edge";
const getVerificationsHourly = 0
const getActiveKeysHourly = 0
const getVerificationsDaily = 0
const getActiveKeysDaily = 0
// export default async function StaffsIndexPage(props: {
export default function StaffsIndexPage(props: {
params: { apiId: string };
searchParams: {
interval?: Interval;
};
}) {
// const data = await queryStaffList({
// id: Number(props.params.apiId)
// })
// console.log("-------------", data)
const [clientReady, setClientReady] = useState<boolean>(false);
const [staffsInfo, setStaffsInfo] = useState<StaffsInfoWithoutId>();
useEffect(() => {
// console.log("------------email", infoRef.current, userData.auth_token)
async function initFunc() {
const data = await queryStaffList({
id: Number(props.params.apiId)
})
if (data.data.list.length > 0) {
setStaffsInfo(data.data.list[0])
}
setClientReady(true)
}
initFunc()
}, []);
// const tenantId = getTenantId();
// const api = await db.query.apis.findFirst({
// where: (table, { eq, and, isNull }) =>
// and(eq(table.id, props.params.apiId), isNull(table.deletedAt)),
// with: {
// workspace: true,
// },
// });
// if (!api || api.workspace.tenantId !== tenantId) {
// return redirect("/new");
// }
const interval = props.searchParams.interval ?? "7d";
const t = new Date();
t.setUTCDate(1);
t.setUTCHours(0, 0, 0, 0);
const billingCycleStart = t.getTime();
const billingCycleEnd = t.setUTCMonth(t.getUTCMonth() + 1) - 1;
const { getVerificationsPerInterval, getActiveKeysPerInterval, start, end, granularity } =
prepareInterval(interval);
// const query = {
// workspaceId: api.workspaceId,
// apiId: api.id,
// start,
// end,
// };
// const [
// keys,
// verifications,
// activeKeys,
// activeKeysTotal,
// _activeKeysInBillingCycle,
// verificationsInBillingCycle,
// ] = await Promise.all([
// db
// .select({ count: sql<number>`count(*)` })
// .from(schema.keys)
// .where(and(eq(schema.keys.keyAuthId, api.keyAuthId!), isNull(schema.keys.deletedAt)))
// .execute()
// .then((res) => res.at(0)?.count ?? 0),
// getVerificationsPerInterval(query),
// getActiveKeysPerInterval(query),
// getActiveKeys(query),
// getActiveKeys({
// workspaceId: api.workspaceId,
// apiId: api.id,
// start: billingCycleStart,
// end: billingCycleEnd,
// }).then((res) => res.data.at(0)),
// getVerificationsPerInterval({
// workspaceId: api.workspaceId,
// apiId: api.id,
// start: billingCycleStart,
// end: billingCycleEnd,
// }),
// ]);
const successOverTime: { x: string; y: number }[] = [];
const ratelimitedOverTime: { x: string; y: number }[] = [];
const usageExceededOverTime: { x: string; y: number }[] = [];
// for (const d of verifications.data.sort((a, b) => a.time - b.time)) {
// const x = new Date(d.time).toISOString();
// successOverTime.push({ x, y: d.success });
// ratelimitedOverTime.push({ x, y: d.rateLimited });
// usageExceededOverTime.push({ x, y: d.usageExceeded });
// }
const verificationsData = [
...successOverTime.map((d) => ({
...d,
category: "Successful Verifications",
})),
...ratelimitedOverTime.map((d) => ({ ...d, category: "Ratelimited" })),
...usageExceededOverTime.map((d) => ({ ...d, category: "Usage Exceeded" })),
];
// const activeKeysOverTime = activeKeys.data.map(({ time, keys }) => ({
// x: new Date(time).toISOString(),
// y: keys,
// }));
let URL = `${process.env.NEXT_PUBLIC_GW_BASE}/staffai/${props.params.apiId}`
return (
<div className="flex flex-col gap-4">
<PageHeader
title={staffsInfo?.org_name}
description={staffsInfo?.description || ""}
actions={[
<Badge
key="apiId"
variant="secondary"
className="flex justify-between w-full gap-2 font-mono font-medium ph-no-capture"
>
{/* {props.params.apiId} */}
{URL}
<CopyButton value={URL} />
</Badge>,
// <CreateKeyButton keyAuthId={"api.keyAuthId!"} />,
]}
/>
{/* <Card>
<CardContent className="grid grid-cols-4 divide-x">
<Metric label="总知识库" value={formatNumber(0)} />
<Metric
label={`使用 ${new Date().toLocaleString("zh-CN", {
month: "long",
})}`}
value={formatNumber(
// verificationsInBillingCycle.data.reduce((sum, day) => sum + day.success, 0),
0
)}
/>
<Metric
label={`激活 ${new Date().toLocaleString("zh-CN", {
month: "long",
})}`}
value={formatNumber(0)}
/>
</CardContent>
</Card> */}
{/* <div className="flex items-center justify-between">
<h2 className="text-2xl font-semibold leading-none tracking-tight">Verifications</h2>
<div>
<IntervalSelect defaultSelected={interval} />
</div>
</div> */}
{/* <PepositoryPage /> */}
<UserList />
<Separator className="my-8" />
<EmptyPlaceholder>
<EmptyPlaceholder.Icon>
<BarChart />
</EmptyPlaceholder.Icon>
<EmptyPlaceholder.Title>No usage</EmptyPlaceholder.Title>
<EmptyPlaceholder.Description>
Verify a xxxxx or change the range
</EmptyPlaceholder.Description>
</EmptyPlaceholder>
</div>
);
}
function prepareInterval(interval: Interval) {
const now = new Date();
switch (interval) {
case "24h": {
const end = now.setUTCHours(now.getUTCHours() + 1, 0, 0, 0);
const intervalMs = 1000 * 60 * 60 * 24;
return {
start: end - intervalMs,
end,
intervalMs,
granularity: 1000 * 60 * 60,
getVerificationsPerInterval: getVerificationsHourly,
getActiveKeysPerInterval: getActiveKeysHourly,
};
}
case "7d": {
now.setUTCDate(now.getUTCDate() + 1);
const end = now.setUTCHours(0, 0, 0, 0);
const intervalMs = 1000 * 60 * 60 * 24 * 7;
return {
start: end - intervalMs,
end,
intervalMs,
granularity: 1000 * 60 * 60 * 24,
getVerificationsPerInterval: getVerificationsDaily,
getActiveKeysPerInterval: getActiveKeysDaily,
};
}
case "30d": {
now.setUTCDate(now.getUTCDate() + 1);
const end = now.setUTCHours(0, 0, 0, 0);
const intervalMs = 1000 * 60 * 60 * 24 * 30;
return {
start: end - intervalMs,
end,
intervalMs,
granularity: 1000 * 60 * 60 * 24,
getVerificationsPerInterval: getVerificationsDaily,
getActiveKeysPerInterval: getActiveKeysDaily,
};
}
case "90d": {
now.setUTCDate(now.getUTCDate() + 1);
const end = now.setUTCHours(0, 0, 0, 0);
const intervalMs = 1000 * 60 * 60 * 24 * 90;
return {
start: end - intervalMs,
end,
intervalMs,
granularity: 1000 * 60 * 60 * 24,
getVerificationsPerInterval: getVerificationsDaily,
getActiveKeysPerInterval: getActiveKeysDaily,
};
}
}
}
const Metric: React.FC<{ label: string; value: string }> = ({ label, value }) => {
return (
<div className="flex flex-col items-start justify-center px-4 py-2">
<p className="text-sm text-content-subtle">{label}</p>
<div className="text-2xl font-semibold leading-none tracking-tight">{value}</div>
</div>
);
};