feat: обновить название проекта и улучшить документацию
Docker images / prepare-release (push) Successful in 5s
Docker images / backend-image (push) Successful in 1m32s
Docker images / frontend-image (push) Successful in 1m37s
Docker images / updater-image (push) Successful in 38s
Docker images / notify-webhook (push) Has been skipped
Docker images / publish-release (push) Successful in 6s

- Изменено название проекта с "RouterLists" на "MikrotikManager" во всех соответствующих файлах и компонентах.
- Обновлены ссылки и комментарии в документации, чтобы отразить новое название и улучшить ясность.
- Внесены изменения в правила коммитов, чтобы обеспечить использование русского языка в сообщениях коммитов.
This commit is contained in:
Denozordec
2026-05-12 17:53:53 +07:00
parent 35876fcd6f
commit c2a8a08618
19 changed files with 360 additions and 29 deletions
+1 -1
View File
@@ -227,7 +227,7 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
<RouterIcon className="size-4" />
</div>
<div className="flex flex-col leading-tight group-data-[collapsible=icon]:hidden">
<span className="font-semibold text-sidebar-foreground text-sm tracking-tight">RouterLists</span>
<span className="font-semibold text-sidebar-foreground text-sm tracking-tight">MikrotikManager</span>
<span className="text-[10px] font-mono text-sidebar-foreground/40">{appVersionLabel}</span>
</div>
</div>
+102
View File
@@ -0,0 +1,102 @@
"use client"
import * as React from "react"
import Link from "next/link"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog"
import { formatAppVersionLabel, getAppVersion } from "@/lib/app-version"
import type { ReleaseManifest } from "@/lib/release-manifest"
import { hasSeenReleaseModal, markReleaseModalSeen } from "@/lib/release-modal-storage"
export function ReleaseNotesModal() {
const [open, setOpen] = React.useState(false)
const [manifest, setManifest] = React.useState<ReleaseManifest | null>(null)
React.useEffect(() => {
let cancelled = false
async function loadManifest() {
try {
const response = await fetch("/release-manifest.json", { cache: "no-store" })
if (!response.ok) return
const data = (await response.json()) as ReleaseManifest
if (cancelled || !data.commits?.length || hasSeenReleaseModal(data.tag)) return
setManifest(data)
setOpen(true)
} catch {
// Локальная сборка или недоступный manifest — модалку не показываем.
}
}
void loadManifest()
return () => {
cancelled = true
}
}, [])
const handleOpenChange = (nextOpen: boolean) => {
setOpen(nextOpen)
if (!nextOpen && manifest) {
markReleaseModalSeen(manifest.tag)
}
}
const versionLabel = formatAppVersionLabel(manifest?.version ?? getAppVersion())
return (
<Dialog open={open} onOpenChange={handleOpenChange}>
<DialogContent className="flex max-h-[min(85vh,720px)] flex-col gap-0 overflow-hidden p-0 sm:max-w-[560px]">
<DialogHeader className="gap-1 border-b px-6 py-4">
<DialogTitle>Что нового в {versionLabel}</DialogTitle>
<DialogDescription>
Краткая сводка изменений текущего релиза MikrotikManager.
</DialogDescription>
</DialogHeader>
<div className="flex-1 overflow-y-auto px-6 py-4">
{manifest?.commits.length ? (
<ul className="flex flex-col gap-3">
{manifest.commits.map((commit) => (
<li
key={commit.sha}
className="flex flex-col gap-2 rounded-lg border bg-card px-3 py-2.5"
>
<div className="flex flex-wrap items-center gap-2">
<Badge variant="secondary" className="font-mono uppercase">
{commit.type}
</Badge>
<span className="font-mono text-xs text-muted-foreground">{commit.shortSha}</span>
<span className="text-xs text-muted-foreground">{commit.author}</span>
</div>
<p className="text-sm leading-relaxed">{commit.subject}</p>
</li>
))}
</ul>
) : null}
</div>
<DialogFooter className="border-t bg-muted/30 px-6 py-4 sm:justify-between">
<Button
variant="outline"
render={<Link href="/releases" />}
onClick={() => handleOpenChange(false)}
>
Все релизы
</Button>
<Button onClick={() => handleOpenChange(false)}>Понятно</Button>
</DialogFooter>
</DialogContent>
</Dialog>
)
}
+160
View File
@@ -0,0 +1,160 @@
"use client"
import * as React from "react"
import { Dialog as DialogPrimitive } from "@base-ui/react/dialog"
import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"
import { XIcon } from "lucide-react"
function Dialog({ ...props }: DialogPrimitive.Root.Props) {
return <DialogPrimitive.Root data-slot="dialog" {...props} />
}
function DialogTrigger({ ...props }: DialogPrimitive.Trigger.Props) {
return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />
}
function DialogPortal({ ...props }: DialogPrimitive.Portal.Props) {
return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />
}
function DialogClose({ ...props }: DialogPrimitive.Close.Props) {
return <DialogPrimitive.Close data-slot="dialog-close" {...props} />
}
function DialogOverlay({
className,
...props
}: DialogPrimitive.Backdrop.Props) {
return (
<DialogPrimitive.Backdrop
data-slot="dialog-overlay"
className={cn(
"fixed inset-0 isolate z-50 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0",
className
)}
{...props}
/>
)
}
function DialogContent({
className,
children,
showCloseButton = true,
...props
}: DialogPrimitive.Popup.Props & {
showCloseButton?: boolean
}) {
return (
<DialogPortal>
<DialogOverlay />
<DialogPrimitive.Popup
data-slot="dialog-content"
className={cn(
"fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-popover p-4 text-sm text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
className
)}
{...props}
>
{children}
{showCloseButton && (
<DialogPrimitive.Close
data-slot="dialog-close"
render={
<Button
variant="ghost"
className="absolute top-2 right-2"
size="icon-sm"
/>
}
>
<XIcon
/>
<span className="sr-only">Close</span>
</DialogPrimitive.Close>
)}
</DialogPrimitive.Popup>
</DialogPortal>
)
}
function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="dialog-header"
className={cn("flex flex-col gap-2", className)}
{...props}
/>
)
}
function DialogFooter({
className,
showCloseButton = false,
children,
...props
}: React.ComponentProps<"div"> & {
showCloseButton?: boolean
}) {
return (
<div
data-slot="dialog-footer"
className={cn(
"-mx-4 -mb-4 flex flex-col-reverse gap-2 rounded-b-xl border-t bg-muted/50 p-4 sm:flex-row sm:justify-end",
className
)}
{...props}
>
{children}
{showCloseButton && (
<DialogPrimitive.Close render={<Button variant="outline" />}>
Close
</DialogPrimitive.Close>
)}
</div>
)
}
function DialogTitle({ className, ...props }: DialogPrimitive.Title.Props) {
return (
<DialogPrimitive.Title
data-slot="dialog-title"
className={cn(
"font-heading text-base leading-none font-medium",
className
)}
{...props}
/>
)
}
function DialogDescription({
className,
...props
}: DialogPrimitive.Description.Props) {
return (
<DialogPrimitive.Description
data-slot="dialog-description"
className={cn(
"text-sm text-muted-foreground *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground",
className
)}
{...props}
/>
)
}
export {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogOverlay,
DialogPortal,
DialogTitle,
DialogTrigger,
}