first commit
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
"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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user