Files
2026-09-08 07:21:34 -04:00

111 lines
4.0 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import React from "react";
import Link from "next/link";
import { usePathname } from "next/navigation";
import {
LayoutDashboard,
ClipboardList,
Search,
Code2,
GitBranch,
Bot,
GitFork,
Rocket,
ShieldCheck,
Activity,
Plug,
BarChart3,
Settings2,
Sparkles,
} from "lucide-react";
import { cn } from "@/lib/utils";
export interface NavItem {
href: string;
label: string;
icon: React.ComponentType<{ className?: string }>;
badge?: string;
}
export const NAV: NavItem[] = [
{ href: "/", label: "Overview", icon: LayoutDashboard },
{ href: "/requirements", label: "Requirements", icon: ClipboardList },
{ href: "/context", label: "Context Explorer", icon: Search },
{ href: "/code", label: "Code", icon: Code2 },
{ href: "/traceability", label: "Traceability", icon: GitBranch },
{ href: "/ask-asf", label: "Ask ASF", icon: Sparkles, badge: "AI" },
{ href: "/jobs", label: "Jobs", icon: Activity },
{ href: "/ai-agents", label: "AI Agents", icon: Bot, badge: "α" },
{ href: "/pipelines", label: "Pipelines", icon: GitFork },
{ href: "/deployments", label: "Deployments", icon: Rocket },
{ href: "/quality", label: "Quality", icon: ShieldCheck },
{ href: "/observability", label: "Observability", icon: Activity },
{ href: "/integrations", label: "Integrations", icon: Plug },
{ href: "/usage", label: "Usage", icon: BarChart3 },
{ href: "/settings", label: "Settings", icon: Settings2 },
];
export function Sidebar() {
const path = usePathname();
return (
<aside className="hidden lg:flex w-64 shrink-0 border-r border-slate-200/80 bg-white/60 backdrop-blur-sm flex-col">
<div className="h-16 flex items-center gap-3 px-5 border-b border-slate-200/80">
<div className="w-9 h-9 rounded-lg bg-gradient-to-br from-brand-500 to-accent shadow-soft grid place-items-center text-white font-bold">
A
</div>
<div className="min-w-0">
<div className="text-sm font-semibold text-slate-800 leading-tight">
{process.env.NEXT_PUBLIC_APP_NAME || "ASF"}
</div>
<div className="text-[11px] text-slate-500 leading-tight">
Software Factory
</div>
</div>
</div>
<nav className="flex-1 overflow-y-auto p-3 space-y-0.5">
{NAV.map(({ href, label, icon: Icon, badge }) => {
const active = href === "/" ? path === "/" : path.startsWith(href);
return (
<Link
key={href}
href={href}
className={cn("asf-nav-item", active && "asf-nav-item-active")}
>
<Icon className="w-4.5 h-4.5 w-[18px] h-[18px] shrink-0" />
<span className="flex-1">{label}</span>
{badge && (
<span className="asf-chip border-accent/20 bg-accent/10 text-accent-muted !px-1.5">
{badge}
</span>
)}
</Link>
);
})}
</nav>
<div className="p-3 border-t border-slate-200/80">
<button
onClick={() => {
// keyboard shortcut is handled in layout; this button also triggers.
const ev = new CustomEvent("asf:open-command");
window.dispatchEvent(ev);
}}
className="w-full asf-btn-secondary justify-between !px-3"
type="button"
>
<span className="flex items-center gap-2">
<Sparkles className="w-4 h-4 text-accent" />
<span className="text-slate-700">Ask ASF…</span>
</span>
<kbd className="asf-chip !text-[10px]">⌘ K</kbd>
</button>
<p className="mt-3 text-[11px] text-slate-400 leading-relaxed">
Tip: every lifecycle element in ASF is an <span className="font-mono text-slate-600 bg-slate-100 rounded px-1">Artifact</span>.
</p>
</div>
</aside>
);
}