Files
tusk/src/components/layout/Sidebar.tsx
Aleksey Shakhmatov 4e5714b291 feat: redesign UI with Twilight design system
Outfit + JetBrains Mono typography, soft dark palette with blue
undertones, electric teal primary, purple-branded AI features,
noise texture, glow effects, glassmorphism, and refined grid/tree.
2026-04-06 10:27:20 +03:00

101 lines
3.7 KiB
TypeScript

import { useState } from "react";
import { useQueryClient } from "@tanstack/react-query";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { SchemaTree } from "@/components/schema/SchemaTree";
import { HistoryPanel } from "@/components/history/HistoryPanel";
import { SavedQueriesPanel } from "@/components/saved-queries/SavedQueriesPanel";
import { AdminPanel } from "@/components/management/AdminPanel";
import { Search, RefreshCw, Layers, Clock, Bookmark, Shield } from "lucide-react";
type SidebarView = "schema" | "history" | "saved" | "admin";
const SCHEMA_QUERY_KEYS = [
"databases", "schemas", "tables", "views",
"functions", "sequences", "completionSchema", "column-details",
];
const SIDEBAR_TABS: { id: SidebarView; label: string; icon: React.ReactNode }[] = [
{ id: "schema", label: "Schema", icon: <Layers className="h-3.5 w-3.5" /> },
{ id: "history", label: "History", icon: <Clock className="h-3.5 w-3.5" /> },
{ id: "saved", label: "Saved", icon: <Bookmark className="h-3.5 w-3.5" /> },
{ id: "admin", label: "Admin", icon: <Shield className="h-3.5 w-3.5" /> },
];
export function Sidebar() {
const [view, setView] = useState<SidebarView>("schema");
const [search, setSearch] = useState("");
const queryClient = useQueryClient();
const handleRefreshSchema = () => {
for (const key of SCHEMA_QUERY_KEYS) {
queryClient.invalidateQueries({ queryKey: [key] });
}
};
return (
<div className="flex h-full flex-col" style={{ background: "var(--sidebar)" }}>
{/* Sidebar navigation tabs */}
<div className="flex border-b border-border/50">
{SIDEBAR_TABS.map((tab) => (
<button
key={tab.id}
className={`relative flex flex-1 items-center justify-center gap-1.5 px-2 py-2 text-[11px] font-medium tracking-wide transition-colors ${
view === tab.id
? "text-foreground tusk-sidebar-tab-active"
: "text-muted-foreground hover:text-foreground/70"
}`}
onClick={() => setView(tab.id)}
>
{tab.icon}
<span className="hidden min-[220px]:inline">{tab.label}</span>
</button>
))}
</div>
{view === "schema" ? (
<>
<div className="flex items-center gap-1 p-2">
<div className="relative flex-1">
<Search className="absolute left-2 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground/60" />
<Input
placeholder="Search objects..."
className="h-7 border-border/40 bg-background/50 pl-7 text-xs placeholder:text-muted-foreground/40 focus:border-primary/40 focus:ring-primary/20"
value={search}
onChange={(e) => setSearch(e.target.value)}
/>
</div>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon-xs"
onClick={handleRefreshSchema}
className="text-muted-foreground hover:text-foreground"
>
<RefreshCw className="h-3.5 w-3.5" />
</Button>
</TooltipTrigger>
<TooltipContent side="bottom">Refresh schema</TooltipContent>
</Tooltip>
</div>
<div className="min-h-0 flex-1 overflow-y-auto overflow-x-hidden">
<SchemaTree />
</div>
</>
) : view === "history" ? (
<HistoryPanel />
) : view === "saved" ? (
<SavedQueriesPanel />
) : (
<AdminPanel />
)}
</div>
);
}