93 lines
3.9 KiB
TypeScript
93 lines
3.9 KiB
TypeScript
"use client";
|
|
|
|
import { useState } from "react";
|
|
import { usePathname, useRouter } from "next/navigation";
|
|
import { useSdk, useFeatureFlags } from "@lynkd/messaging-inbox-sdk";
|
|
import { useUi } from "@/lib/ui-state";
|
|
import { IconButton } from "@/components/ui/primitives";
|
|
import { Home, MessageSquare, Inbox as InboxIcon, Bell, LifeBuoy, MoreHorizontal, Plus } from "lucide-react";
|
|
import clsx from "clsx";
|
|
|
|
/** Slack-style far-left rail: workspace switcher + primary destinations. */
|
|
export function WorkspaceRail() {
|
|
const { bootstrap, brandName } = useSdk();
|
|
const flags = useFeatureFlags();
|
|
const router = useRouter();
|
|
const pathname = usePathname();
|
|
const { setNotifOpen, openModal, toast } = useUi();
|
|
|
|
const workspaces = bootstrap?.workspaces ?? [];
|
|
const [activeWs, setActiveWs] = useState<string | undefined>(bootstrap?.workspace?.id);
|
|
|
|
const nav = [
|
|
{ icon: Home, label: "Home", href: "/", match: (p: string) => p === "/" || p.startsWith("/c/") },
|
|
flags.messaging && { icon: MessageSquare, label: "DMs", href: "/dms", match: (p: string) => p.startsWith("/dms") },
|
|
flags.inbox && { icon: InboxIcon, label: "Inbox", href: "/inbox", match: (p: string) => p.startsWith("/inbox") },
|
|
flags.support && { icon: LifeBuoy, label: "Support", href: "/support", match: (p: string) => p.startsWith("/support") },
|
|
].filter(Boolean) as { icon: typeof Home; label: string; href: string; match: (p: string) => boolean }[];
|
|
|
|
const active = activeWs ?? bootstrap?.workspace?.id;
|
|
|
|
return (
|
|
<nav className="hidden w-[68px] shrink-0 flex-col items-center gap-1 border-r border-border bg-bg py-3 md:flex">
|
|
{flags.workspaceSwitcher && (
|
|
<div className="flex flex-col items-center gap-2 pb-2">
|
|
{workspaces.map((w) => (
|
|
<button
|
|
key={w.id}
|
|
title={w.name}
|
|
onClick={() => {
|
|
setActiveWs(w.id);
|
|
if (w.id !== bootstrap?.workspace?.id) toast(`Switched to ${w.name} (demo)`, "success");
|
|
}}
|
|
className={clsx(
|
|
"focus-ring grid h-11 w-11 place-items-center rounded-[14px] text-sm font-bold transition-all",
|
|
w.id === active ? "text-white ring-2 ring-accent" : "text-ink/80 hover:rounded-[12px]",
|
|
)}
|
|
style={{ background: w.id === active ? "var(--brand-gradient)" : "var(--c-elevated)" }}
|
|
>
|
|
{w.initials}
|
|
</button>
|
|
))}
|
|
<IconButton label="Add workspace" onClick={() => openModal("create-workspace")}>
|
|
<Plus size={18} />
|
|
</IconButton>
|
|
<div className="my-1 h-px w-8 bg-border" />
|
|
</div>
|
|
)}
|
|
|
|
<div className="flex min-h-0 flex-1 flex-col items-center gap-1 overflow-y-auto no-scrollbar">
|
|
{nav.map((n) => {
|
|
const isActive = n.match(pathname);
|
|
return (
|
|
<button
|
|
key={n.href}
|
|
title={n.label}
|
|
onClick={() => router.push(n.href)}
|
|
className={clsx(
|
|
"focus-ring flex h-12 w-12 flex-col items-center justify-center gap-0.5 rounded-control text-[10px] font-medium transition-colors",
|
|
isActive ? "bg-accent-soft text-accent" : "text-muted hover:bg-hover hover:text-ink",
|
|
)}
|
|
>
|
|
<n.icon size={20} />
|
|
<span>{n.label}</span>
|
|
</button>
|
|
);
|
|
})}
|
|
</div>
|
|
|
|
<div className="flex flex-col items-center gap-1">
|
|
{flags.notifications && (
|
|
<IconButton label="Notifications" onClick={() => setNotifOpen(true)}>
|
|
<Bell size={20} />
|
|
</IconButton>
|
|
)}
|
|
<IconButton label="Help & more" onClick={() => toast("Help center — coming soon", "default")}>
|
|
<MoreHorizontal size={20} />
|
|
</IconButton>
|
|
</div>
|
|
<span className="sr-only">{brandName}</span>
|
|
</nav>
|
|
);
|
|
}
|