89 lines
2.9 KiB
TypeScript
89 lines
2.9 KiB
TypeScript
import type { Metadata } from "next";
|
|
import Link from "next/link";
|
|
import { notFound } from "next/navigation";
|
|
import { canReadPage } from "@/lib/auth/permissions";
|
|
import { logPageAccess } from "@/lib/auth/log-page-access";
|
|
import { requireSession } from "@/lib/auth/require-session";
|
|
import { MatchLogColoredBody } from "@/components/match-log-colored-body";
|
|
import {
|
|
parseMatchIdParam,
|
|
readMatchLogFile,
|
|
} from "@/lib/match-logs-server";
|
|
import { redirect } from "next/navigation";
|
|
|
|
export async function generateMetadata({
|
|
params,
|
|
}: {
|
|
params: Promise<{ matchId: string }>;
|
|
}): Promise<Metadata> {
|
|
const { matchId } = await params;
|
|
return {
|
|
title: `Match ${matchId} log · Kick Kings Admin`,
|
|
};
|
|
}
|
|
|
|
export default async function MatchLogPage({
|
|
params,
|
|
}: {
|
|
params: Promise<{ matchId: string }>;
|
|
}) {
|
|
const account = await requireSession();
|
|
if (
|
|
!canReadPage(account, "matches") &&
|
|
!canReadPage(account, "analysis")
|
|
) {
|
|
redirect("/");
|
|
}
|
|
|
|
const { matchId: raw } = await params;
|
|
const matchId = parseMatchIdParam(raw);
|
|
if (matchId == null) {
|
|
notFound();
|
|
}
|
|
|
|
await logPageAccess(account, "match-log", { matchId });
|
|
|
|
const result = await readMatchLogFile(matchId);
|
|
|
|
return (
|
|
<div className="flex min-h-dvh flex-col bg-[#0d1117] text-zinc-100">
|
|
<header className="flex shrink-0 items-center gap-2 border-b border-zinc-700/80 bg-[#161b22] px-3 py-2">
|
|
<div className="flex gap-1.5 pr-2" aria-hidden="true">
|
|
<span className="h-3 w-3 rounded-full bg-[#ff5f56]" />
|
|
<span className="h-3 w-3 rounded-full bg-[#ffbd2e]" />
|
|
<span className="h-3 w-3 rounded-full bg-[#27c93f]" />
|
|
</div>
|
|
<p className="min-w-0 flex-1 truncate font-mono text-xs text-zinc-300">
|
|
<span className="text-emerald-400/90">soccar</span>
|
|
<span className="text-zinc-500"> — </span>
|
|
<span className="text-zinc-100">match_{matchId}.txt</span>
|
|
</p>
|
|
<Link
|
|
href="/?tab=matches"
|
|
className="shrink-0 rounded border border-zinc-600 bg-zinc-800 px-2.5 py-1 font-mono text-xs text-zinc-200 transition hover:bg-zinc-700 hover:text-white"
|
|
>
|
|
Back to matches
|
|
</Link>
|
|
</header>
|
|
|
|
<div className="min-h-0 flex-1 overflow-auto p-4">
|
|
{!result.ok ? (
|
|
<p className="font-mono text-sm text-red-400">
|
|
<span className="text-red-500/80">error:</span> {result.message}
|
|
</p>
|
|
) : result.content === "" ? (
|
|
<p className="font-mono text-sm text-zinc-600">(empty file)</p>
|
|
) : (
|
|
<MatchLogColoredBody content={result.content} />
|
|
)}
|
|
</div>
|
|
|
|
<footer className="shrink-0 border-t border-zinc-800 bg-[#161b22] px-3 py-1.5">
|
|
<p className="font-mono text-[11px] text-zinc-500">
|
|
<span className="text-emerald-600/80">⏎</span> UTF-8
|
|
</p>
|
|
</footer>
|
|
</div>
|
|
);
|
|
}
|