import { useCallback, useEffect, useMemo, useState } from "react"; import { Paperclip, Plus } from "lucide-react"; import { useQueries } from "@tanstack/react-query"; import { DndContext, closestCenter, PointerSensor, useSensor, useSensors, type DragEndEvent, } from "@dnd-kit/core"; import { SortableContext, useSortable, verticalListSortingStrategy, arrayMove, } from "@dnd-kit/sortable"; import { CSS } from "@dnd-kit/utilities"; import { useCompany } from "../context/CompanyContext"; import { useDialog } from "../context/DialogContext"; import { cn } from "../lib/utils"; import { queryKeys } from "../lib/queryKeys"; import { sidebarBadgesApi } from "../api/sidebarBadges"; import { heartbeatsApi } from "../api/heartbeats"; import { useLocation, useNavigate } from "@/lib/router"; import { Tooltip, TooltipContent, TooltipTrigger, } from "@/components/ui/tooltip"; import type { Company } from "@paperclipai/shared"; import { CompanyPatternIcon } from "./CompanyPatternIcon"; const ORDER_STORAGE_KEY = "paperclip.companyOrder"; function getStoredOrder(): string[] { try { const raw = localStorage.getItem(ORDER_STORAGE_KEY); if (raw) return JSON.parse(raw); } catch { /* ignore */ } return []; } function saveOrder(ids: string[]) { localStorage.setItem(ORDER_STORAGE_KEY, JSON.stringify(ids)); } /** Sort companies by stored order, appending any new ones at the end. */ function sortByStoredOrder(companies: Company[]): Company[] { const order = getStoredOrder(); if (order.length === 0) return companies; const byId = new Map(companies.map((c) => [c.id, c])); const sorted: Company[] = []; for (const id of order) { const c = byId.get(id); if (c) { sorted.push(c); byId.delete(id); } } // Append any companies not in stored order for (const c of byId.values()) { sorted.push(c); } return sorted; } function SortableCompanyItem({ company, isSelected, hasLiveAgents, hasUnreadInbox, onSelect, }: { company: Company; isSelected: boolean; hasLiveAgents: boolean; hasUnreadInbox: boolean; onSelect: () => void; }) { const { attributes, listeners, setNodeRef, transform, transition, isDragging, } = useSortable({ id: company.id }); const style = { transform: CSS.Transform.toString(transform), transition, zIndex: isDragging ? 10 : undefined, opacity: isDragging ? 0.8 : 1, }; return (
); } export function CompanyRail() { const { companies, selectedCompanyId, setSelectedCompanyId } = useCompany(); const { openOnboarding } = useDialog(); const navigate = useNavigate(); const location = useLocation(); const isInstanceRoute = location.pathname.startsWith("/instance/"); const highlightedCompanyId = isInstanceRoute ? null : selectedCompanyId; const sidebarCompanies = useMemo( () => companies.filter((company) => company.status !== "archived"), [companies], ); const companyIds = useMemo(() => sidebarCompanies.map((company) => company.id), [sidebarCompanies]); const liveRunsQueries = useQueries({ queries: companyIds.map((companyId) => ({ queryKey: queryKeys.liveRuns(companyId), queryFn: () => heartbeatsApi.liveRunsForCompany(companyId), refetchInterval: 10_000, })), }); const sidebarBadgeQueries = useQueries({ queries: companyIds.map((companyId) => ({ queryKey: queryKeys.sidebarBadges(companyId), queryFn: () => sidebarBadgesApi.get(companyId), refetchInterval: 15_000, })), }); const hasLiveAgentsByCompanyId = useMemo(() => { const result = new MapAdd company