Ребренд Мониторинг Орбита -> Железный Егорин; accent green->amber #F59E0B; LoginScreen правая половина = Lightning (WebGL, hue 35 тёплый) под стеклянной формой; левая панель — тёплый градиент. Доступ агентам: репо публичный, push german-токеном. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
114 lines
4.4 KiB
TypeScript
114 lines
4.4 KiB
TypeScript
import { useMemo } from 'react';
|
|
import { FolderGit2, GitBranch, Lock, Globe, ExternalLink, Sparkles, CircleSlash } from 'lucide-react';
|
|
import { PageHeader } from '@/components/PageHeader';
|
|
import { Card, CardHeader, Badge } from '@/components/ui/primitives';
|
|
import { KpiCard } from '@/components/ui/KpiCard';
|
|
import { cn } from '@/lib/utils';
|
|
import {
|
|
repos,
|
|
repoUrl,
|
|
CATEGORY_LABEL,
|
|
CATEGORY_ORDER,
|
|
GITEA_BASE,
|
|
type GiteaRepo,
|
|
} from '@/data/repos';
|
|
|
|
function RepoRow({ r }: { r: GiteaRepo }) {
|
|
return (
|
|
<a
|
|
href={repoUrl(r)}
|
|
target="_blank"
|
|
rel="noreferrer"
|
|
className="group flex items-center gap-3 py-2.5 transition-colors hover:bg-muted/40"
|
|
>
|
|
<FolderGit2 className={cn('h-4 w-4 shrink-0', r.empty ? 'text-fg-muted' : 'text-accent')} />
|
|
<div className="min-w-0 flex-1">
|
|
<div className="flex items-center gap-1.5">
|
|
<span className="truncate text-sm font-medium text-fg">{r.name}</span>
|
|
{r.isNew && (
|
|
<Badge severity="ok">
|
|
<Sparkles className="h-3 w-3" /> новый
|
|
</Badge>
|
|
)}
|
|
{r.empty && (
|
|
<Badge severity="warning">
|
|
<CircleSlash className="h-3 w-3" /> пусто
|
|
</Badge>
|
|
)}
|
|
</div>
|
|
<div className="truncate text-[11px] text-fg-muted">{r.note}</div>
|
|
</div>
|
|
<span className="hidden items-center gap-1 text-[11px] text-fg-muted sm:inline-flex">
|
|
<GitBranch className="h-3.5 w-3.5" />
|
|
<span className={cn('font-mono', r.branch === 'master' && 'text-warn')}>{r.branch}</span>
|
|
</span>
|
|
{r.private ? (
|
|
<Lock className="h-3.5 w-3.5 shrink-0 text-fg-muted" aria-label="приватный" />
|
|
) : (
|
|
<Globe className="h-3.5 w-3.5 shrink-0 text-ok" aria-label="публичный" />
|
|
)}
|
|
<ExternalLink className="h-3.5 w-3.5 shrink-0 text-transparent group-hover:text-fg-muted" />
|
|
</a>
|
|
);
|
|
}
|
|
|
|
export function ReposModule() {
|
|
const stats = useMemo(() => {
|
|
const total = repos.length;
|
|
const pub = repos.filter((r) => !r.private).length;
|
|
const master = repos.filter((r) => r.branch === 'master').length;
|
|
const empty = repos.filter((r) => r.empty).length;
|
|
return { total, priv: total - pub, pub, master, empty };
|
|
}, []);
|
|
|
|
const byCat = useMemo(
|
|
() => CATEGORY_ORDER.map((cat) => ({ cat, items: repos.filter((r) => r.category === cat) })),
|
|
[],
|
|
);
|
|
|
|
return (
|
|
<div>
|
|
<PageHeader
|
|
icon={FolderGit2}
|
|
title="Карта репозиториев"
|
|
desc="Gitea git1.servaki.online · owner german · канон = git"
|
|
actions={
|
|
<a
|
|
href={GITEA_BASE}
|
|
target="_blank"
|
|
rel="noreferrer"
|
|
className="inline-flex items-center gap-1.5 rounded-lg border border-border bg-surface px-3 py-1.5 text-xs font-medium text-fg-muted transition-colors hover:text-fg"
|
|
>
|
|
<ExternalLink className="h-3.5 w-3.5" /> Открыть Gitea
|
|
</a>
|
|
}
|
|
/>
|
|
|
|
<div className="mb-4 grid grid-cols-2 gap-3 sm:grid-cols-4">
|
|
<KpiCard label="Всего репо" value={stats.total} digits={0} icon={FolderGit2} tone="accent" />
|
|
<KpiCard label="Приватных" value={stats.priv} digits={0} icon={Lock} tone="muted" />
|
|
<KpiCard label="Публичных" value={stats.pub} digits={0} icon={Globe} tone="ok" sub="только Heritage" />
|
|
<KpiCard label="Ветка master" value={stats.master} digits={0} icon={GitBranch} tone="warn" sub="cascade · vpn-clients" />
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 gap-4 lg:grid-cols-2">
|
|
{byCat.map(({ cat, items }) => (
|
|
<Card key={cat}>
|
|
<CardHeader title={CATEGORY_LABEL[cat]} right={<Badge severity="info">{items.length}</Badge>} />
|
|
<div className="divide-y divide-border px-4 pb-2">
|
|
{items.map((r) => (
|
|
<RepoRow key={r.name} r={r} />
|
|
))}
|
|
</div>
|
|
</Card>
|
|
))}
|
|
</div>
|
|
|
|
<p className="mt-4 text-center text-xs text-fg-muted">
|
|
Источник истины — git1. Карта сверена вручную 2026-06-30 ({stats.total} репо, {stats.empty} пустой). Пуш — через
|
|
<span className="font-mono"> http.extraHeader</span>, токен в URL не сохраняется.
|
|
</p>
|
|
</div>
|
|
);
|
|
}
|