55 lines
1.8 KiB
TypeScript
55 lines
1.8 KiB
TypeScript
import type { Metadata } from "next";
|
|
import Link from "next/link";
|
|
import { JetBrains_Mono, DM_Sans } from "next/font/google";
|
|
import { gitStatus } from "@/lib/git";
|
|
import "./manager.css";
|
|
|
|
export const dynamic = "force-dynamic";
|
|
|
|
const mono = JetBrains_Mono({ subsets: ["latin"], variable: "--mono", display: "swap" });
|
|
const sans = DM_Sans({ subsets: ["latin"], variable: "--sans", display: "swap" });
|
|
|
|
export const metadata: Metadata = {
|
|
title: "Blog Manager",
|
|
description: "Local editor for the portfolio blog",
|
|
};
|
|
|
|
async function PublishBadge() {
|
|
const status = await gitStatus();
|
|
if (!status.available) {
|
|
return <span className="badge err" title={status.error}>git unavailable</span>;
|
|
}
|
|
const pending = status.changes.length;
|
|
const ahead = status.ahead ?? 0;
|
|
if (!pending && !ahead) return <span className="badge ok">published</span>;
|
|
return (
|
|
<Link href="/publish" className="badge warn">
|
|
{pending ? `${pending} unpublished change${pending === 1 ? "" : "s"}` : `${ahead} commit(s) not pushed`}
|
|
</Link>
|
|
);
|
|
}
|
|
|
|
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
return (
|
|
<html lang="en" className={`${mono.variable} ${sans.variable}`}>
|
|
<body>
|
|
<div className="shell">
|
|
<header className="topbar">
|
|
<Link href="/" className="brand">blog-manager</Link>
|
|
<nav>
|
|
<Link href="/">posts</Link>
|
|
<Link href="/posts/new">new post</Link>
|
|
<Link href="/categories">categories</Link>
|
|
<Link href="/tags">tags</Link>
|
|
<Link href="/publish">publish</Link>
|
|
</nav>
|
|
<span className="spacer" />
|
|
<PublishBadge />
|
|
</header>
|
|
{children}
|
|
</div>
|
|
</body>
|
|
</html>
|
|
);
|
|
}
|