Add PWA meta tags for iOS home screen. Fix mobile properties drawer with safe area insets. Add image attachment button to comment thread. Improve sidebar with collapsible sections, project grouping, and mobile bottom nav. Show token and billing type breakdown on costs page. Fix inbox loading state to show content progressively. Various mobile overflow and layout fixes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
103 lines
3.7 KiB
TypeScript
103 lines
3.7 KiB
TypeScript
import {
|
|
Inbox,
|
|
CircleDot,
|
|
Target,
|
|
LayoutDashboard,
|
|
DollarSign,
|
|
History,
|
|
Search,
|
|
SquarePen,
|
|
Network,
|
|
} from "lucide-react";
|
|
import { useQuery } from "@tanstack/react-query";
|
|
import { SidebarSection } from "./SidebarSection";
|
|
import { SidebarNavItem } from "./SidebarNavItem";
|
|
import { SidebarProjects } from "./SidebarProjects";
|
|
import { SidebarAgents } from "./SidebarAgents";
|
|
import { useDialog } from "../context/DialogContext";
|
|
import { useCompany } from "../context/CompanyContext";
|
|
import { sidebarBadgesApi } from "../api/sidebarBadges";
|
|
import { heartbeatsApi } from "../api/heartbeats";
|
|
import { queryKeys } from "../lib/queryKeys";
|
|
import { Button } from "@/components/ui/button";
|
|
import { ScrollArea } from "@/components/ui/scroll-area";
|
|
|
|
export function Sidebar() {
|
|
const { openNewIssue } = useDialog();
|
|
const { selectedCompanyId, selectedCompany } = useCompany();
|
|
const { data: sidebarBadges } = useQuery({
|
|
queryKey: queryKeys.sidebarBadges(selectedCompanyId!),
|
|
queryFn: () => sidebarBadgesApi.get(selectedCompanyId!),
|
|
enabled: !!selectedCompanyId,
|
|
});
|
|
const { data: liveRuns } = useQuery({
|
|
queryKey: queryKeys.liveRuns(selectedCompanyId!),
|
|
queryFn: () => heartbeatsApi.liveRunsForCompany(selectedCompanyId!),
|
|
enabled: !!selectedCompanyId,
|
|
refetchInterval: 10_000,
|
|
});
|
|
const liveRunCount = liveRuns?.length ?? 0;
|
|
|
|
function openSearch() {
|
|
document.dispatchEvent(new KeyboardEvent("keydown", { key: "k", metaKey: true }));
|
|
}
|
|
|
|
return (
|
|
<aside className="w-60 h-full min-h-0 border-r border-border bg-background flex flex-col">
|
|
{/* Top bar: Company name (bold) + Search — aligned with top sections (no visible border) */}
|
|
<div className="flex items-center gap-1 px-3 h-12 shrink-0">
|
|
<span className="flex-1 text-sm font-bold text-foreground truncate pl-1">
|
|
{selectedCompany?.name ?? "Select company"}
|
|
</span>
|
|
<Button
|
|
variant="ghost"
|
|
size="icon-sm"
|
|
className="text-muted-foreground shrink-0"
|
|
onClick={openSearch}
|
|
>
|
|
<Search className="h-4 w-4" />
|
|
</Button>
|
|
</div>
|
|
|
|
<ScrollArea className="flex-1">
|
|
<nav className="flex flex-col gap-4 px-3 py-2">
|
|
<div className="flex flex-col gap-0.5">
|
|
{/* New Issue button aligned with nav items */}
|
|
<button
|
|
onClick={() => openNewIssue()}
|
|
className="flex items-center gap-2.5 px-3 py-2 text-[13px] font-medium text-muted-foreground hover:bg-accent/50 hover:text-foreground transition-colors"
|
|
>
|
|
<SquarePen className="h-4 w-4 shrink-0" />
|
|
<span className="truncate">New Issue</span>
|
|
</button>
|
|
<SidebarNavItem to="/dashboard" label="Dashboard" icon={LayoutDashboard} liveCount={liveRunCount} />
|
|
<SidebarNavItem
|
|
to="/inbox"
|
|
label="Inbox"
|
|
icon={Inbox}
|
|
badge={sidebarBadges?.inbox}
|
|
badgeTone={sidebarBadges?.failedRuns ? "danger" : "default"}
|
|
alert={(sidebarBadges?.failedRuns ?? 0) > 0}
|
|
/>
|
|
</div>
|
|
|
|
<SidebarProjects />
|
|
|
|
<SidebarSection label="Work">
|
|
<SidebarNavItem to="/issues" label="Issues" icon={CircleDot} />
|
|
<SidebarNavItem to="/goals" label="Goals" icon={Target} />
|
|
</SidebarSection>
|
|
|
|
<SidebarAgents />
|
|
|
|
<SidebarSection label="Company">
|
|
<SidebarNavItem to="/org" label="Org" icon={Network} />
|
|
<SidebarNavItem to="/costs" label="Costs" icon={DollarSign} />
|
|
<SidebarNavItem to="/activity" label="Activity" icon={History} />
|
|
</SidebarSection>
|
|
</nav>
|
|
</ScrollArea>
|
|
</aside>
|
|
);
|
|
}
|