feat: 3.01 header logos+nav, 3.09 dynamic contact types, 3.10 hot-desk window, 3.11 vault email+link
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { useMemo } from "react";
|
||||
import { useMemo, useState, useCallback, useEffect } from "react";
|
||||
import * as Icons from "lucide-react";
|
||||
import { buildNavigation } from "@/config/navigation";
|
||||
import { COMPANIES } from "@/config/companies";
|
||||
@@ -64,24 +64,61 @@ function NavItem({
|
||||
);
|
||||
}
|
||||
|
||||
function SidebarLogo() {
|
||||
const sdt = COMPANIES["studii-de-teren"];
|
||||
const LOGO_COMPANIES = ["studii-de-teren", "urban-switch"] as const;
|
||||
|
||||
const logoSrc = sdt.logo?.light ?? null;
|
||||
function SidebarLogos() {
|
||||
const [clickCount, setClickCount] = useState(0);
|
||||
const [shuffled, setShuffled] = useState(false);
|
||||
|
||||
if (!logoSrc) {
|
||||
return <Icons.LayoutDashboard className="h-5 w-5 text-primary" />;
|
||||
}
|
||||
const handleLogoClick = useCallback(() => {
|
||||
setClickCount((prev) => {
|
||||
const next = prev + 1;
|
||||
if (next >= 3) {
|
||||
setShuffled((s) => !s);
|
||||
return 0;
|
||||
}
|
||||
return next;
|
||||
});
|
||||
}, []);
|
||||
|
||||
// Reset click count after 2 seconds of inactivity
|
||||
useEffect(() => {
|
||||
if (clickCount === 0) return;
|
||||
const timer = setTimeout(() => setClickCount(0), 2000);
|
||||
return () => clearTimeout(timer);
|
||||
}, [clickCount]);
|
||||
|
||||
const logos = shuffled ? [...LOGO_COMPANIES].reverse() : [...LOGO_COMPANIES];
|
||||
|
||||
return (
|
||||
<Image
|
||||
src={logoSrc}
|
||||
alt={sdt.shortName}
|
||||
width={28}
|
||||
height={28}
|
||||
className="h-7 w-7 shrink-0"
|
||||
suppressHydrationWarning
|
||||
/>
|
||||
<div className="flex items-center gap-1.5">
|
||||
{logos.map((companyId) => {
|
||||
const company = COMPANIES[companyId];
|
||||
const logoSrc = company?.logo?.light;
|
||||
if (!logoSrc) return null;
|
||||
return (
|
||||
<button
|
||||
key={companyId}
|
||||
type="button"
|
||||
onClick={handleLogoClick}
|
||||
className={cn(
|
||||
"relative shrink-0 rounded-md p-0.5 transition-all duration-300 hover:scale-110 focus:outline-none",
|
||||
shuffled && "animate-pulse",
|
||||
)}
|
||||
title={company.shortName}
|
||||
>
|
||||
<Image
|
||||
src={logoSrc}
|
||||
alt={company.shortName}
|
||||
width={36}
|
||||
height={36}
|
||||
className="h-9 w-9 object-contain"
|
||||
suppressHydrationWarning
|
||||
/>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -91,10 +128,14 @@ export function Sidebar() {
|
||||
|
||||
return (
|
||||
<aside className="flex h-full w-64 shrink-0 flex-col border-r bg-card">
|
||||
<div className="flex h-14 items-center gap-2 border-b px-4">
|
||||
<SidebarLogo />
|
||||
<Link
|
||||
href="/"
|
||||
className="flex h-14 items-center gap-2.5 border-b px-4 transition-colors hover:bg-accent/30"
|
||||
title="Panou principal"
|
||||
>
|
||||
<SidebarLogos />
|
||||
<span className="text-lg font-semibold">ArchiTools</span>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
<ScrollArea className="flex-1 px-3 py-3">
|
||||
<Link
|
||||
|
||||
Reference in New Issue
Block a user