import { type Component, For, Show } from "solid-js"; import { LayoutDashboard, ListTodo, Settings, Clock, ArrowUpCircle, PanelLeftClose, Snowflake, Pickaxe, ChevronDown, Gauge, TrendingUp, Users, Box, BarChart3, HelpCircle } from "lucide-solid"; import { cn } from "@/lib/utils"; import { Button } from "./ui/button"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; import { store, currentTaskContext, setCurrentTaskContext, loadTasksForOwner } from "@/store"; import { pb } from "@/lib/pocketbase"; import { createSignal, createEffect } from "solid-js"; interface NavItem { icon: any; label: string; view: string; } // Desktop nav items (all) const desktopNavItems: NavItem[] = [ { icon: ListTodo, label: "Focus", view: "critical" }, { icon: Clock, label: "Urgency", view: "urgency" }, { icon: ArrowUpCircle, label: "Priority", view: "priority" }, { icon: LayoutDashboard, label: "Matrix", view: "matrix" }, { icon: Snowflake, label: "Snowball", view: "snowball" }, { icon: Pickaxe, label: "Dig In", view: "dig_in" }, { icon: BarChart3, label: "Progress", view: "progress" }, ]; // Mobile nav groups interface MobileNavGroup { icon: any; label: string; items?: NavItem[]; view?: string; // Direct navigation (no sub-items) } const mobileNavGroups: MobileNavGroup[] = [ { icon: ListTodo, label: "Focus", view: "critical" }, { icon: LayoutDashboard, label: "Matrix", view: "matrix" }, { icon: TrendingUp, label: "Value", items: [ { icon: ArrowUpCircle, label: "Priority", view: "priority" }, { icon: Clock, label: "Urgency", view: "urgency" }, ] }, { icon: Gauge, label: "Flow", items: [ { icon: Snowflake, label: "Snowball", view: "snowball" }, { icon: Pickaxe, label: "Dig In", view: "dig_in" }, { icon: BarChart3, label: "Progress", view: "progress" }, ] }, { icon: Settings, label: "Settings", view: "settings" }, ]; export const BottomNav: Component<{ currentView: string; setView: (v: string) => void }> = (props) => { const isGroupActive = (group: MobileNavGroup) => { if (group.view) return props.currentView === group.view; return group.items?.some(item => props.currentView === item.view) ?? false; }; return ( {(group) => ( group.view && props.setView(group.view)} class={cn( "flex flex-col items-center justify-center w-full h-full space-y-1 transition-colors", isGroupActive(group) ? "text-primary" : "text-muted-foreground" )} > {group.label} }> {group.label} {(item) => ( props.setView(item.view)} > {item.label} )} )} ); }; export const ContextSwitcher: Component<{ isLocked?: boolean; isPeeking?: boolean; setIsPeeking?: (v: boolean) => void; onOpenChange?: (open: boolean) => void; isMobile?: boolean; }> = (props) => { const [open, setOpen] = createSignal(false); // Sync with parent if needed createEffect(() => { props.onOpenChange?.(open()); }); // Close popover when sidebar disappears (Desktop only) createEffect(() => { if (!props.isMobile && !props.isLocked && !props.isPeeking) { setOpen(false); } }); const ctx = currentTaskContext; // Accessor const personalContext = () => store.contexts.find(context => !context.deletedAt && context.kind === "user" && context.targetUserId === pb.authStore.model?.id ) || null; const isPersonalContext = () => { const current = ctx(); const personal = personalContext(); return current === 'mine' || ( typeof current === "object" && "bucketId" in current && !!personal && current.bucketId === personal.id ); }; const label = () => { const c = ctx(); return isPersonalContext() ? "My Bucket" : (c === 'mine' ? "My Bucket" : c.name); }; const handleSwitch = (id: string, name: string, type: 'mine' | 'user' | 'bucket' = 'mine') => { if (type === 'mine') { const personal = personalContext(); if (personal) { setCurrentTaskContext({ bucketId: personal.id, name: "My Bucket", isPersonal: true }); } else { setCurrentTaskContext('mine'); } } else if (type === 'bucket') { setCurrentTaskContext({ bucketId: id, name }); } else { setCurrentTaskContext({ userId: id, name }); void loadTasksForOwner(id); } // Close popover setOpen(false); // Collapse sidebar if peaking (Desktop only) if (!props.isMobile && props.setIsPeeking && !props.isLocked && props.isPeeking) { props.setIsPeeking(false); } }; const hasContexts = () => !!personalContext() || store.subscribedBuckets.length > 0 || store.supervisedContexts.length > 0; return ( {label()} > }> {!isPersonalContext() && ( )} handleSwitch('mine', '', 'mine')} class={cn( "w-full flex items-center gap-2 p-2 rounded-md text-sm transition-colors", isPersonalContext() ? "bg-primary/10 text-primary font-semibold" : "hover:bg-muted text-foreground" )} > My Bucket 0}> Pinned Global Buckets {store.buckets.filter(b => store.subscribedBuckets.includes(b.id)).length} store.subscribedBuckets.includes(b.id))}> {(bucket) => ( handleSwitch(bucket.id, bucket.name, 'bucket')} class={cn( "w-full flex items-center gap-2 p-2 rounded-md text-sm transition-colors", (ctx() as any).bucketId === bucket.id ? "bg-muted text-foreground font-semibold ring-1 ring-border" : "hover:bg-muted text-foreground" )} > {bucket.name} )} 0}> Supervised Contexts {store.supervisedContexts.length} {(supervised) => ( handleSwitch(supervised.ownerUserId, supervised.ownerName, 'user')} class={cn( "w-full flex items-center gap-2 p-2 rounded-md text-sm transition-colors", (ctx() as any).userId === supervised.ownerUserId ? "bg-orange-500/10 text-orange-600 font-semibold" : "hover:bg-muted text-foreground" )} > {supervised.ownerName} )} ); }; export const Sidebar: Component<{ currentView: string; setView: (v: string) => void; isLocked: boolean; setIsLocked: (v: boolean) => void; isPeeking: boolean; setIsPeeking: (v: boolean) => void; isEmbed?: boolean; onDropdownOpenChange?: (open: boolean) => void; }> = (props) => { const [switcherOpen, setSwitcherOpen] = createSignal(false); return ( ); };