import type { ReactNode } from "react"; import Link from "next/link"; import { chatGPTSignInPath, chatGPTSignOutPath } from "../chatgpt-auth"; import { getCloudViewer, type CloudViewer } from "../cloud-auth"; import { getActiveBeta, getServiceStatusSnapshot, type ServiceStatusSnapshot, } from "@/db/repository"; import { ConnectivityBanner } from "./ConnectivityBanner"; const serviceStatusLabels = { operational: "服务正常", maintenance: "计划维护", degraded: "服务降级", outage: "服务中断", } as const; export function Brand({ compact = false }: { compact?: boolean }) { return ( {!compact && ( NekoNest Cloud )} ); } export function StatusPill({ tone = "neutral", children, }: { tone?: "good" | "warn" | "danger" | "neutral" | "info"; children: ReactNode; }) { return {children}; } export async function PublicShell({ children, serviceStatus: providedServiceStatus, }: { children: ReactNode; serviceStatus?: ServiceStatusSnapshot; }) { const [viewer, beta, serviceStatus] = await Promise.all([ getCloudViewer(), getActiveBeta(), providedServiceStatus ?? getServiceStatusSnapshot(), ]); return (
公开原型 · {beta ? "公测免费" : "公测已结束"} {viewer ? ( 打开控制台 ) : ( 登录控制台 )}
{children}
); } const dashboardNav = [ { href: "/dashboard", label: "总览", icon: "⌂" }, { href: "/dashboard/hosts", label: "主机", icon: "▣" }, { href: "/dashboard/billing", label: "公测权益", icon: "○" }, { href: "/dashboard/security", label: "安全与设备", icon: "◇" }, { href: "/dashboard/feedback", label: "问题反馈", icon: "?" }, ] as const; export async function DashboardShell({ viewer, active, children, serviceStatus: providedServiceStatus, }: { viewer: CloudViewer; active: string; children: ReactNode; serviceStatus?: ServiceStatusSnapshot; }) { const serviceStatus = providedServiceStatus ?? (await getServiceStatusSnapshot()); return (
{serviceStatus.activeIncidents.length > 0 && (
{serviceStatusLabels[serviceStatus.status]} {serviceStatus.activeIncidents[0].title} 查看详情 →
)} {viewer.isLocalDemo && (
当前使用本地演示身份;正式托管环境会要求登录,演示数据不会代表真实在线服务。
)} {children}
); } export function PageHeading({ eyebrow, title, description, actions, }: { eyebrow?: string; title: string; description: string; actions?: ReactNode; }) { return (
{eyebrow && {eyebrow}}

{title}

{description}

{actions &&
{actions}
}
); } export function formatMoney(amountMinor: number, currency = "CNY") { return new Intl.NumberFormat("zh-CN", { style: "currency", currency, minimumFractionDigits: 0, maximumFractionDigits: 2, }).format(amountMinor / 100); } export function formatDate(value: string | null, includeTime = false) { if (!value) return "未设定"; return new Intl.DateTimeFormat("zh-CN", { year: "numeric", month: "short", day: "numeric", ...(includeTime ? { hour: "2-digit", minute: "2-digit" } : {}), }).format(new Date(value)); }