@@ -139,10 +139,21 @@ function Board() {
);
}
-export function KanbanBoard({ initialCategories }: { initialCategories: CategoryDTO[] }) {
+export function KanbanBoard({
+ initialCategories,
+ projectId,
+ title = "Home",
+}: {
+ initialCategories: CategoryDTO[];
+ // Undefined renders the caller's Home board; set it to scope the whole
+ // board -- creating/reordering categories, and everything nested under
+ // them -- to that Project instead.
+ projectId?: string;
+ title?: string;
+}) {
return (
-
-
+
+
);
}
diff --git a/components/nav/projects-nav-section.tsx b/components/nav/projects-nav-section.tsx
new file mode 100644
index 0000000..6572b00
--- /dev/null
+++ b/components/nav/projects-nav-section.tsx
@@ -0,0 +1,112 @@
+"use client";
+
+import { useEffect, useState } from "react";
+import Link from "next/link";
+import { usePathname } from "next/navigation";
+import { ChevronDown, ChevronRight, FolderKanban } from "lucide-react";
+
+import { cn } from "@/lib/utils";
+import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
+import { useProjects } from "@/components/projects/projects-context";
+
+const STORAGE_KEY = "organize:projects-expanded";
+
+/**
+ * The "Projects" nav entry: a link to /projects (the list + create page)
+ * plus, underneath it, a live list of the user's projects that expands
+ * and contracts independently of the link itself -- clicking the label
+ * navigates, clicking the chevron just toggles the list.
+ */
+export function ProjectsNavSection({ collapsed }: { collapsed: boolean }) {
+ const pathname = usePathname();
+ const { projects } = useProjects();
+ // Defaults to expanded on both server and first client render (same
+ // reasoning as SideNavProvider's `collapsed`) to avoid a hydration
+ // mismatch; the persisted value applies right after mount.
+ const [expanded, setExpanded] = useState(true);
+
+ useEffect(() => {
+ const stored = localStorage.getItem(STORAGE_KEY);
+ if (stored !== null) setExpanded(stored === "true");
+ }, []);
+
+ function toggleExpanded() {
+ setExpanded((prev) => {
+ const next = !prev;
+ localStorage.setItem(STORAGE_KEY, String(next));
+ return next;
+ });
+ }
+
+ const isListActive = pathname === "/projects";
+
+ const link = (
+
+
+ {!collapsed && Projects}
+
+ );
+
+ // No room for an expandable sub-list in the icon rail -- just a link,
+ // matching how every other collapsed nav item behaves.
+ if (collapsed) {
+ return (
+
+
+ Projects
+
+ );
+ }
+
+ return (
+
+
+ {link}
+
+
+
+ {expanded && projects.length > 0 && (
+
+ {projects.map((project) => {
+ const isActive = pathname === `/projects/${project.id}`;
+ return (
+
+ {project.title}
+
+ );
+ })}
+
+ )}
+
+ );
+}
diff --git a/components/nav/side-nav.tsx b/components/nav/side-nav.tsx
index 7c314c1..53a8416 100644
--- a/components/nav/side-nav.tsx
+++ b/components/nav/side-nav.tsx
@@ -8,6 +8,7 @@ import { Separator } from "@/components/ui/separator";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { useSideNav } from "@/components/nav/side-nav-provider";
import { SideNavItem } from "@/components/nav/side-nav-item";
+import { ProjectsNavSection } from "@/components/nav/projects-nav-section";
import { NAV_ITEMS, ADMIN_NAV_ITEM } from "@/components/nav/nav-items";
import { ThemeToggle } from "@/components/theme/theme-toggle";
import { logout } from "@/lib/actions/auth";
@@ -15,7 +16,7 @@ import { Role } from "@/lib/generated/prisma/enums";
export function SideNav({ userEmail, role }: { userEmail: string; role: Role }) {
const { collapsed, toggle } = useSideNav();
- const items = role === Role.ADMIN ? [...NAV_ITEMS, ADMIN_NAV_ITEM] : NAV_ITEMS;
+ const adminItem = role === Role.ADMIN ? [ADMIN_NAV_ITEM] : [];
return (
-