diff --git a/scripts/fix-sidebar.js b/scripts/fix-sidebar.js new file mode 100644 index 0000000..0445c46 --- /dev/null +++ b/scripts/fix-sidebar.js @@ -0,0 +1,258 @@ +const fs = require('fs') +const path = require('path') + +// Find the actual project root by looking for package.json +const candidates = [ + '/vercel/share/v0-project', + process.cwd(), +] + +let root = null +for (const c of candidates) { + if (fs.existsSync(path.join(c, 'package.json'))) { + root = c + break + } +} + +if (!root) { + // Walk up from cwd + let dir = process.cwd() + while (dir !== '/') { + if (fs.existsSync(path.join(dir, 'package.json'))) { root = dir; break } + dir = path.dirname(dir) + } +} + +console.log('Project root:', root) +const target = path.join(root, 'components', 'app-sidebar.tsx') +console.log('Target file:', target) +console.log('Exists:', fs.existsSync(target)) + +if (!fs.existsSync(target)) { + // Try to find it + function findFile(dir, name) { + try { + const entries = fs.readdirSync(dir, { withFileTypes: true }) + for (const e of entries) { + if (e.name === 'node_modules' || e.name === '.git') continue + const full = path.join(dir, e.name) + if (e.isDirectory()) { + const found = findFile(full, name) + if (found) return found + } else if (e.name === name) return full + } + } catch {} + return null + } + const found = findFile(root || '/', 'app-sidebar.tsx') + console.log('Found at:', found) + process.exit(1) +} + +let src = fs.readFileSync(target, 'utf8') + +// Fix 1: add useEffect to import +src = src.replace( + `import { useState } from 'react'`, + `import { useState, useEffect } from 'react'` +) + +// Fix 2: add useEffect after useState call +src = src.replace( + `const [drawerOpen, setDrawerOpen] = useState(false)`, + `const [drawerOpen, setDrawerOpen] = useState(false) + + // Auto-close drawer on route change — no onClick needed on nav links + useEffect(() => { setDrawerOpen(false) }, [pathname])` +) + +// Fix 3: remove onClick from NavLink interface +src = src.replace(` onClick?: () => void\n`, '') + +// Fix 4: remove onClick from NavLink function params +src = src.replace( + `function NavLink({ href, label, icon: Icon, badge, active, onClick }: NavLinkProps) {`, + `function NavLink({ href, label, icon: Icon, badge, active }: NavLinkProps) {` +) + +// Fix 5: remove onClick={onClick} from +src = src.replace(` onClick={onClick}\n`, '') + +// Fix 6: remove onClick from all NavLink usages +src = src.replace(/ onClick=\{onNav\}/g, '') + +fs.writeFileSync(target, src, 'utf8') +console.log('Done! Wrote', src.length, 'bytes to', target) + + +import Link from 'next/link' +import { usePathname } from 'next/navigation' +import { useState, useEffect } from 'react' +import { cn } from '@/lib/utils' +import { signOut } from '@/lib/auth-actions' +import type { SpaceMember, Space } from '@/lib/types' +import { + Menu, X, LayoutDashboard, ListChecks, FolderKanban, Settings2, + MessageSquare, Users, CreditCard, BookUser, Download, LogOut, +} from 'lucide-react' + +function NavLink({ + href, label, icon: Icon, badge, active, +}: { + href: string + label: string + icon: React.ElementType + badge?: number + active: boolean +}) { + return ( + + + + {label} + + {badge && badge > 0 ? ( + + {badge} + + ) : null} + + ) +} + +interface AppSidebarProps { + member: SpaceMember & { spaces: Space } + taskBadge?: number + commsBadge?: number + paymentBadge?: number +} + +export function AppSidebar({ member, taskBadge = 0, commsBadge = 0, paymentBadge = 0 }: AppSidebarProps) { + const pathname = usePathname() + const space = member.spaces + const initials = member.display_name?.split(' ').map((n) => n[0]).join('').toUpperCase().slice(0, 2) || '??' + const isAdmin = member.role === 'admin' || member.role === 'board' + const [drawerOpen, setDrawerOpen] = useState(false) + + // Auto-close the drawer whenever the route changes + useEffect(() => { + setDrawerOpen(false) + }, [pathname]) + + function isActive(href) { + return pathname === href || pathname.startsWith(href + '/') + } + + async function handleSignOut() { + await signOut() + } + + const navJsx = ( + <> +
+ +
+ {'{'}hs{'}'} +
+ +

{space?.name || 'My Space'}

+
+ + + +
+ +
+ + ) + + return ( + <> + + +
+ +
+ hs +
+ {space?.name} + + +
+ + {drawerOpen && ( +
+
setDrawerOpen(false)} /> +
+
+ {space?.name} + +
+ {navJsx} +
+
+ )} + + ) +} +` + +fs.writeFileSync('/vercel/share/v0-project/components/app-sidebar.tsx', content, 'utf8') +console.log('Done. Wrote', content.length, 'bytes') diff --git a/scripts/fix-sidebar.mjs b/scripts/fix-sidebar.mjs new file mode 100644 index 0000000..8b1d061 --- /dev/null +++ b/scripts/fix-sidebar.mjs @@ -0,0 +1,180 @@ +import fs from 'fs' + +const content = `'use client' + +import Link from 'next/link' +import { usePathname } from 'next/navigation' +import { useState, useEffect } from 'react' +import { cn } from '@/lib/utils' +import { signOut } from '@/lib/auth-actions' +import type { SpaceMember, Space } from '@/lib/types' +import { + Menu, X, LayoutDashboard, ListChecks, FolderKanban, Settings2, + MessageSquare, Users, CreditCard, BookUser, Download, LogOut, +} from 'lucide-react' + +function NavLink({ + href, label, icon: Icon, badge, active, +}: { + href: string + label: string + icon: React.ElementType + badge?: number + active: boolean +}) { + return ( + + + + {label} + + {badge && badge > 0 ? ( + + {badge} + + ) : null} + + ) +} + +interface AppSidebarProps { + member: SpaceMember & { spaces: Space } + taskBadge?: number + commsBadge?: number + paymentBadge?: number +} + +export function AppSidebar({ member, taskBadge = 0, commsBadge = 0, paymentBadge = 0 }: AppSidebarProps) { + const pathname = usePathname() + const space = member.spaces + const initials = member.display_name?.split(' ').map((n: string) => n[0]).join('').toUpperCase().slice(0, 2) || '??' + const isAdmin = member.role === 'admin' || member.role === 'board' + const [drawerOpen, setDrawerOpen] = useState(false) + + // Auto-close the drawer whenever the route changes + useEffect(() => { + setDrawerOpen(false) + }, [pathname]) + + function isActive(href: string) { + return pathname === href || pathname.startsWith(href + '/') + } + + async function handleSignOut() { + await signOut() + } + + // navJsx is JSX stored in a variable — NOT a nested component. + // This avoids React remounting it on every parent render, which was + // causing the drawer to unmount before Next.js could navigate. + const navJsx = ( + <> +
+ +
+ {'{'}hs{'}'} +
+ +

{space?.name || 'My Space'}

+
+ + + +
+ +
+ + ) + + return ( + <> + {/* Desktop sidebar */} + + + {/* Mobile: fixed top bar */} +
+ +
+ hs +
+ {space?.name} + + +
+ + {/* Mobile: slide-out drawer */} + {drawerOpen && ( +
+
setDrawerOpen(false)} aria-hidden="true" /> +
+
+ {space?.name} + +
+ {navJsx} +
+
+ )} + + ) +} +` + +fs.writeFileSync('/vercel/share/v0-project/components/app-sidebar.tsx', content, 'utf8') +console.log('Wrote app-sidebar.tsx successfully, length:', content.length)