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 } from "@/store"; import { pb } from "@/lib/pocketbase"; import { createResource, 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 currentUserId = pb.authStore.model?.id; 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 [oversightUsers] = createResource( () => [...new Set(store.shareRules.filter(r => r.targetUserId === currentUserId && r.type === 'all').map(r => r.ownerId))], async (ownerIds) => { if (ownerIds.length === 0) return []; try { // Fetch user names for these IDs in a single query const filter = ownerIds.map(id => `id="${id}"`).join(' || '); const users = await pb.collection('users').getFullList({ filter, fields: 'id,name,email', requestKey: null // Disable auto-cancellation }); return users.map(u => ({ id: u.id, name: u.name || u.email })); } catch (e: any) { if (e.isAbort) return []; // Ignore explicit aborts console.error("Failed to load oversight users", e); return []; } } ); const ctx = currentTaskContext; // Accessor const label = () => { const c = ctx(); return c === 'mine' ? "My Bucket" : c.name; }; const handleSwitch = (id: string, name: string, type: 'mine' | 'user' | 'bucket' = 'mine') => { if (type === 'mine') { setCurrentTaskContext('mine'); } else if (type === 'bucket') { setCurrentTaskContext({ bucketId: id, name }); } else { setCurrentTaskContext({ userId: id, name }); } // Close popover setOpen(false); // Collapse sidebar if peaking (Desktop only) if (!props.isMobile && props.setIsPeeking && !props.isLocked && props.isPeeking) { props.setIsPeeking(false); } }; const hasContexts = () => (oversightUsers() && oversightUsers()!.length > 0) || store.subscribedBuckets.length > 0; return ( {label()} > }> {ctx() !== 'mine' && ( )} handleSwitch('mine', '', 'mine')} class={cn( "w-full flex items-center gap-2 p-2 rounded-md text-sm transition-colors", ctx() === 'mine' ? "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}> Shared User Buckets {oversightUsers()?.length} {(user) => ( handleSwitch(user.id, user.name, 'user')} class={cn( "w-full flex items-center gap-2 p-2 rounded-md text-sm transition-colors", (ctx() as any).userId === user.id ? "bg-orange-500/10 text-orange-600 font-semibold" : "hover:bg-muted text-foreground" )} > {user.name} )} ); }; 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 ( ); };