feat(ui): make company rail items proper links for right-click support
Company rail icons are now <a> tags with href instead of <button>, enabling right-click "Open in new tab". CompanyContext reads a ?company= URL param on init so the correct company is selected when opened in a new tab. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -24,30 +24,10 @@ import {
|
|||||||
TooltipTrigger,
|
TooltipTrigger,
|
||||||
} from "@/components/ui/tooltip";
|
} from "@/components/ui/tooltip";
|
||||||
import type { Company } from "@paperclip/shared";
|
import type { Company } from "@paperclip/shared";
|
||||||
|
import { CompanyPatternIcon } from "./CompanyPatternIcon";
|
||||||
const COMPANY_COLORS = [
|
|
||||||
"#6366f1", // indigo
|
|
||||||
"#8b5cf6", // violet
|
|
||||||
"#ec4899", // pink
|
|
||||||
"#f43f5e", // rose
|
|
||||||
"#f97316", // orange
|
|
||||||
"#eab308", // yellow
|
|
||||||
"#22c55e", // green
|
|
||||||
"#14b8a6", // teal
|
|
||||||
"#06b6d4", // cyan
|
|
||||||
"#3b82f6", // blue
|
|
||||||
];
|
|
||||||
|
|
||||||
const ORDER_STORAGE_KEY = "paperclip.companyOrder";
|
const ORDER_STORAGE_KEY = "paperclip.companyOrder";
|
||||||
|
|
||||||
function companyColor(name: string): string {
|
|
||||||
let hash = 0;
|
|
||||||
for (let i = 0; i < name.length; i++) {
|
|
||||||
hash = name.charCodeAt(i) + ((hash << 5) - hash);
|
|
||||||
}
|
|
||||||
return COMPANY_COLORS[Math.abs(hash) % COMPANY_COLORS.length]!;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getStoredOrder(): string[] {
|
function getStoredOrder(): string[] {
|
||||||
try {
|
try {
|
||||||
const raw = localStorage.getItem(ORDER_STORAGE_KEY);
|
const raw = localStorage.getItem(ORDER_STORAGE_KEY);
|
||||||
@@ -107,15 +87,16 @@ function SortableCompanyItem({
|
|||||||
opacity: isDragging ? 0.8 : 1,
|
opacity: isDragging ? 0.8 : 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
const color = companyColor(company.name);
|
|
||||||
const initial = company.name.charAt(0).toUpperCase();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={setNodeRef} style={style} {...attributes} {...listeners}>
|
<div ref={setNodeRef} style={style} {...attributes} {...listeners}>
|
||||||
<Tooltip delayDuration={300}>
|
<Tooltip delayDuration={300}>
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<button
|
<a
|
||||||
onClick={onSelect}
|
href={`/dashboard?company=${company.id}`}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
onSelect();
|
||||||
|
}}
|
||||||
className="relative flex items-center justify-center group"
|
className="relative flex items-center justify-center group"
|
||||||
>
|
>
|
||||||
{/* Selection indicator pill */}
|
{/* Selection indicator pill */}
|
||||||
@@ -128,18 +109,20 @@ function SortableCompanyItem({
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn("transition-all duration-200", isDragging && "scale-105")}
|
||||||
"flex items-center justify-center w-11 h-11 text-base font-semibold text-white transition-all duration-200",
|
|
||||||
isSelected
|
|
||||||
? "rounded-xl"
|
|
||||||
: "rounded-[22px] group-hover:rounded-xl",
|
|
||||||
isDragging && "shadow-lg scale-105"
|
|
||||||
)}
|
|
||||||
style={{ backgroundColor: color }}
|
|
||||||
>
|
>
|
||||||
{initial}
|
<CompanyPatternIcon
|
||||||
|
companyName={company.name}
|
||||||
|
brandColor={company.brandColor}
|
||||||
|
className={cn(
|
||||||
|
isSelected
|
||||||
|
? "rounded-xl"
|
||||||
|
: "rounded-[22px] group-hover:rounded-xl",
|
||||||
|
isDragging && "shadow-lg",
|
||||||
|
)}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</a>
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipContent side="right" sideOffset={8}>
|
<TooltipContent side="right" sideOffset={8}>
|
||||||
<p>{company.name}</p>
|
<p>{company.name}</p>
|
||||||
|
|||||||
@@ -35,7 +35,22 @@ const CompanyContext = createContext<CompanyContextValue | null>(null);
|
|||||||
export function CompanyProvider({ children }: { children: ReactNode }) {
|
export function CompanyProvider({ children }: { children: ReactNode }) {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const [selectedCompanyId, setSelectedCompanyIdState] = useState<string | null>(
|
const [selectedCompanyId, setSelectedCompanyIdState] = useState<string | null>(
|
||||||
() => localStorage.getItem(STORAGE_KEY)
|
() => {
|
||||||
|
// Check URL param first (supports "open in new tab" from company rail)
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
const companyParam = urlParams.get("company");
|
||||||
|
if (companyParam) {
|
||||||
|
localStorage.setItem(STORAGE_KEY, companyParam);
|
||||||
|
// Clean up the URL param
|
||||||
|
urlParams.delete("company");
|
||||||
|
const newSearch = urlParams.toString();
|
||||||
|
const newUrl =
|
||||||
|
window.location.pathname + (newSearch ? `?${newSearch}` : "");
|
||||||
|
window.history.replaceState({}, "", newUrl);
|
||||||
|
return companyParam;
|
||||||
|
}
|
||||||
|
return localStorage.getItem(STORAGE_KEY);
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const { data: companies = [], isLoading, error } = useQuery({
|
const { data: companies = [], isLoading, error } = useQuery({
|
||||||
|
|||||||
Reference in New Issue
Block a user