Docker images / prepare-release (push) Successful in 4s
Docker images / backend-image (push) Failing after 2m36s
Docker images / frontend-image (push) Successful in 2m22s
Docker images / notify-webhook (push) Skipped
Docker images / updater-image (push) Successful in 55s
Docker images / publish-release (push) Skipped
Подключить App Switcher, NavUser, OpsPanel и AlertDialog вместо Card-shell. Co-authored-by: Cursor <cursoragent@cursor.com>
100 lines
4.1 KiB
TypeScript
100 lines
4.1 KiB
TypeScript
import Link from "next/link"
|
|
import { PageHeader } from "@/components/page-header"
|
|
import { OpsPanel } from "@/components/ops-panel"
|
|
import { buttonVariants } from "@/components/ui/button"
|
|
import { formatAppVersionLabel, getAppVersion, getReleaseUrl } from "@/lib/app-version"
|
|
import {
|
|
formatCommitSubject,
|
|
getReleaseCommitPreview,
|
|
RELEASE_COMMIT_PREVIEW_LIMIT,
|
|
} from "@/lib/release-commits"
|
|
import { readReleaseManifest } from "@/lib/release-manifest"
|
|
import { cn } from "@/lib/utils"
|
|
import { ExternalLinkIcon } from "lucide-react"
|
|
|
|
export const dynamic = "force-dynamic"
|
|
|
|
function formatPublishedAt(value: string | null, isProdBuild: boolean): string {
|
|
if (value) {
|
|
return new Intl.DateTimeFormat("ru-RU", {
|
|
dateStyle: "long",
|
|
timeStyle: "short",
|
|
}).format(new Date(value))
|
|
}
|
|
return isProdBuild ? "Прод-сборка" : "Локальная сборка"
|
|
}
|
|
|
|
export default function ReleasesPage() {
|
|
const manifest = readReleaseManifest()
|
|
const version = getAppVersion()
|
|
const isProdBuild = !version.endsWith("-dev")
|
|
const releaseUrl = getReleaseUrl() || manifest.releaseUrl || null
|
|
const commits = getReleaseCommitPreview(manifest.commits)
|
|
|
|
return (
|
|
<div className="flex flex-col h-full">
|
|
<PageHeader
|
|
crumbs={[{ label: "Система" }, { label: "Релизы" }]}
|
|
actions={
|
|
releaseUrl ? (
|
|
<Link
|
|
href={releaseUrl}
|
|
target="_blank"
|
|
rel="noreferrer"
|
|
className={cn(buttonVariants({ variant: "outline", size: "sm" }), "h-8 inline-flex items-center gap-2")}
|
|
>
|
|
<ExternalLinkIcon className="size-4" />
|
|
Открыть в Gitea
|
|
</Link>
|
|
) : null
|
|
}
|
|
/>
|
|
|
|
<div className="flex-1 overflow-y-auto p-6">
|
|
<div className="max-w-[960px] mx-auto space-y-6">
|
|
<OpsPanel
|
|
title="Текущая версия"
|
|
description="Сборка MikrotikManager, опубликованная через CI/CD."
|
|
contentClassName="flex flex-col gap-3 px-5 pb-5"
|
|
>
|
|
<div className="flex flex-wrap items-center gap-3">
|
|
<span className="text-2xl font-semibold tracking-tight">{formatAppVersionLabel(version)}</span>
|
|
<span className="text-sm text-muted-foreground">{formatPublishedAt(manifest.publishedAt, isProdBuild)}</span>
|
|
</div>
|
|
<p className="text-sm text-muted-foreground">
|
|
Версия формируется автоматически от базы <span className="font-mono">v1.0.0</span> по Conventional Commits.
|
|
</p>
|
|
</OpsPanel>
|
|
|
|
<OpsPanel
|
|
title="Сводка коммитов"
|
|
description={
|
|
commits.length > 0
|
|
? `Последние ${RELEASE_COMMIT_PREVIEW_LIMIT} коммитов в сборке ${formatAppVersionLabel(manifest.version)}.`
|
|
: "Для локальной разработки список коммитов пуст. В прод-сборке он заполняется CI."
|
|
}
|
|
contentClassName="px-5 pb-5"
|
|
>
|
|
{commits.length === 0 ? (
|
|
<p className="text-sm text-muted-foreground">Коммитов для отображения пока нет.</p>
|
|
) : (
|
|
<ul className="flex flex-col gap-3">
|
|
{commits.map((commit) => (
|
|
<li key={commit.sha} className="rounded-md border px-3 py-2">
|
|
<div className="flex flex-wrap items-center gap-2 text-xs text-muted-foreground">
|
|
<span className="font-mono uppercase">{commit.type}</span>
|
|
<span className="font-mono">{commit.shortSha}</span>
|
|
<span>{commit.author}</span>
|
|
</div>
|
|
<p className="mt-1 text-sm">{formatCommitSubject(commit.subject)}</p>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
)}
|
|
</OpsPanel>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|