85 lines
3.3 KiB
TypeScript
85 lines
3.3 KiB
TypeScript
import { useQuery } from "@tanstack/react-query";
|
|
import { Link } from "wouter";
|
|
import { apiRequest } from "@/lib/queryClient";
|
|
import type { UserDetailResponse } from "./types";
|
|
|
|
export default function AdminUserDetail({ userId }: { userId: string }) {
|
|
const detailQuery = useQuery<UserDetailResponse>({
|
|
queryKey: ["admin/users", userId],
|
|
queryFn: async () => {
|
|
const res = await apiRequest("GET", `admin/users/${userId}`);
|
|
return res.json();
|
|
},
|
|
});
|
|
|
|
if (detailQuery.isLoading) {
|
|
return <div className="text-sm text-muted-foreground">Loading user…</div>;
|
|
}
|
|
|
|
if (!detailQuery.data) {
|
|
return <div className="text-sm text-muted-foreground">User not found.</div>;
|
|
}
|
|
|
|
const { user, runs, current_config, events, capital_summary } = detailQuery.data;
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
<div className="rounded-2xl border border-border/60 bg-card/70 p-6 shadow-sm">
|
|
<Link href="/admin/users">
|
|
<a className="text-xs text-primary hover:underline">← Back to users</a>
|
|
</Link>
|
|
<h2 className="mt-2 text-2xl font-semibold">{user.username}</h2>
|
|
<p className="text-xs text-muted-foreground">User ID: {user.user_id}</p>
|
|
</div>
|
|
|
|
<div className="grid gap-4 md:grid-cols-2">
|
|
<div className="rounded-2xl border border-border/60 bg-card/70 p-6 shadow-sm">
|
|
<p className="text-sm font-semibold">Capital Summary</p>
|
|
<div className="mt-3 space-y-1 text-xs text-muted-foreground">
|
|
<p>Cash: {capital_summary.cash ?? "—"}</p>
|
|
<p>Invested: {capital_summary.invested ?? "—"}</p>
|
|
<p>MTM: {capital_summary.mtm ?? "—"}</p>
|
|
<p>Equity: {capital_summary.equity ?? "—"}</p>
|
|
<p>PnL: {capital_summary.pnl ?? "—"}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="rounded-2xl border border-border/60 bg-card/70 p-6 shadow-sm">
|
|
<p className="text-sm font-semibold">Current Config</p>
|
|
<pre className="mt-3 text-xs text-muted-foreground whitespace-pre-wrap">
|
|
{JSON.stringify(current_config, null, 2)}
|
|
</pre>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="rounded-2xl border border-border/60 bg-card/70 p-6 shadow-sm">
|
|
<p className="text-sm font-semibold">Recent Runs</p>
|
|
<div className="mt-4 space-y-2">
|
|
{runs.map((run) => (
|
|
<div key={run.run_id} className="flex items-center justify-between text-xs">
|
|
<Link href={`/admin/runs/${run.run_id}`}>
|
|
<a className="text-primary hover:underline">{run.run_id}</a>
|
|
</Link>
|
|
<span className="text-muted-foreground">{run.status}</span>
|
|
<span className="text-muted-foreground">{run.created_at ?? "—"}</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="rounded-2xl border border-border/60 bg-card/70 p-6 shadow-sm">
|
|
<p className="text-sm font-semibold">Recent Events</p>
|
|
<div className="mt-4 space-y-2">
|
|
{events.map((evt, idx) => (
|
|
<div key={`${evt.ts}-${idx}`} className="text-xs text-muted-foreground">
|
|
<span className="text-foreground">{evt.event}</span>{" "}
|
|
<span>({evt.source})</span>{" "}
|
|
<span>{evt.ts ?? "—"}</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|