← back to jg0fox__sitegeit

Function bodies 34 total

All specs Real LLM only Function bodies
GET function · typescript · L4-L19 (16 LOC)
src/app/(auth)/auth/callback/route.ts
export async function GET(request: Request) {
  const { searchParams, origin } = new URL(request.url)
  const code = searchParams.get('code')
  const next = searchParams.get('next') ?? '/'

  if (code) {
    const supabase = await createClient()
    const { error } = await supabase.auth.exchangeCodeForSession(code)
    if (!error) {
      return NextResponse.redirect(`${origin}${next}`)
    }
  }

  // Auth code exchange failed — redirect to login with error
  return NextResponse.redirect(`${origin}/login`)
}
AuthLayout function · typescript · L1-L11 (11 LOC)
src/app/(auth)/layout.tsx
export default function AuthLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <div className="flex min-h-screen items-center justify-center bg-gray-50 px-4">
      <div className="w-full max-w-sm">{children}</div>
    </div>
  )
}
LoginPage function · typescript · L6-L111 (106 LOC)
src/app/(auth)/login/page.tsx
export default function LoginPage() {
  const [email, setEmail] = useState('')
  const [loading, setLoading] = useState(false)
  const [sent, setSent] = useState(false)
  const [error, setError] = useState<string | null>(null)

  const handleLogin = async (e: React.FormEvent) => {
    e.preventDefault()
    setLoading(true)
    setError(null)

    const supabase = createClient()
    const { error } = await supabase.auth.signInWithOtp({
      email,
      options: {
        emailRedirectTo: `${window.location.origin}/auth/callback`,
      },
    })

    if (error) {
      setError('Could not send magic link. Check your email and try again.')
      setLoading(false)
      return
    }

    setSent(true)
    setLoading(false)
  }

  return (
    <div className="flex flex-col items-center">
      <div className="mb-8 text-center">
        <h1 className="text-2xl font-bold text-gray-900">Sitegeit</h1>
        <p className="mt-1 text-sm text-gray-500">
          Lead pipeline &amp; website g
ClientDetailPage function · typescript · L3-L13 (11 LOC)
src/app/(dashboard)/clients/[id]/page.tsx
export default function ClientDetailPage() {
  return (
    <div className="space-y-6">
      <EmptyState
        icon="person"
        title="Client details"
        description="Client detail view with tier management, site analytics, billing history, and reports will be available in Phase 6."
      />
    </div>
  )
}
ClientsPage function · typescript · L5-L31 (27 LOC)
src/app/(dashboard)/clients/page.tsx
export default function ClientsPage() {
  return (
    <div className="space-y-6">
      <div>
        <p className="text-sm text-gray-500">
          Manage active clients, track revenue, and monitor their sites.
        </p>
      </div>

      <EmptyState
        icon="group"
        title="No clients yet"
        description="Convert prospects to clients to start managing them here. Clients appear once a deal closes."
        action={
          <Button variant="outline" asChild>
            <Link href="/pipeline">
              <span className="material-symbols-outlined text-[18px]">
                conversion_path
              </span>
              View pipeline
            </Link>
          </Button>
        }
      />
    </div>
  )
}
DiscoverPage function · typescript · L4-L28 (25 LOC)
src/app/(dashboard)/discover/page.tsx
export default function DiscoverPage() {
  return (
    <div className="space-y-6">
      <div>
        <p className="text-sm text-gray-500">
          Find local businesses without websites and add them to your pipeline.
        </p>
      </div>

      <EmptyState
        icon="travel_explore"
        title="No search results yet"
        description="Search for businesses by region and category to discover leads without websites."
        action={
          <Button disabled>
            <span className="material-symbols-outlined text-[18px]">
              search
            </span>
            Start your first search
          </Button>
        }
      />
    </div>
  )
}
EmailReviewPage function · typescript · L3-L19 (17 LOC)
src/app/(dashboard)/email-review/page.tsx
export default function EmailReviewPage() {
  return (
    <div className="space-y-6">
      <div>
        <p className="text-sm text-gray-500">
          Review, edit, and approve outreach emails before they send.
        </p>
      </div>

      <EmptyState
        icon="rate_review"
        title="No emails to review"
        description="When the pipeline generates outreach emails, they'll appear here for your review before sending."
      />
    </div>
  )
}
Same scanner, your repo: https://repobility.com — Repobility
DashboardLayout function · typescript · L6-L20 (15 LOC)
src/app/(dashboard)/layout.tsx
export default function DashboardLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <SidebarProvider>
      <div className="min-h-screen bg-gray-50">
        <Sidebar />
        <Header />
        <MainContent>{children}</MainContent>
      </div>
    </SidebarProvider>
  )
}
groupByDate function · typescript · L64-L85 (22 LOC)
src/app/(dashboard)/notifications/page.tsx
function groupByDate(items: NotificationItem[]) {
  const now = new Date()
  const today = new Date(now.getFullYear(), now.getMonth(), now.getDate())
  const yesterday = new Date(today.getTime() - 86400000)
  const weekAgo = new Date(today.getTime() - 7 * 86400000)

  const groups: { label: string; items: NotificationItem[] }[] = [
    { label: 'Today', items: [] },
    { label: 'Yesterday', items: [] },
    { label: 'This week', items: [] },
    { label: 'Earlier', items: [] },
  ]

  items.forEach((item) => {
    if (item.createdAt >= today) groups[0].items.push(item)
    else if (item.createdAt >= yesterday) groups[1].items.push(item)
    else if (item.createdAt >= weekAgo) groups[2].items.push(item)
    else groups[3].items.push(item)
  })

  return groups.filter((g) => g.items.length > 0)
}
NotificationsPage function · typescript · L87-L198 (112 LOC)
src/app/(dashboard)/notifications/page.tsx
export default function NotificationsPage() {
  const [activeTab, setActiveTab] = useState<FilterTab>('all')

  const filtered =
    activeTab === 'all'
      ? MOCK_NOTIFICATIONS
      : MOCK_NOTIFICATIONS.filter(
          (n) => NOTIFICATION_CATEGORY_MAP[n.type] === activeTab
        )

  const groups = groupByDate(filtered)

  return (
    <div className="space-y-6">
      <div className="flex items-center justify-between">
        <p className="text-sm text-gray-500">
          Stay on top of pipeline events, email engagement, and system alerts.
        </p>
        <Button variant="ghost" size="sm">
          Mark all read
        </Button>
      </div>

      {/* Filter tabs */}
      <div className="flex gap-1 rounded-lg bg-gray-100 p-1">
        {TABS.map((tab) => (
          <button
            key={tab.key}
            onClick={() => setActiveTab(tab.key)}
            className={cn(
              'rounded-md px-3 py-1.5 text-sm font-medium transition-colors',
              a
DashboardPage function · typescript · L6-L26 (21 LOC)
src/app/(dashboard)/page.tsx
export default function DashboardPage() {
  return (
    <div className="space-y-6">
      {/* Pipeline overview cards */}
      <PipelineSummary />

      {/* Quick action buttons */}
      <QuickActions />

      {/* Two-column layout: activity + metrics */}
      <div className="grid gap-6 lg:grid-cols-3">
        <div className="lg:col-span-2">
          <RecentActivity />
        </div>
        <div>
          <MRRSnapshot />
        </div>
      </div>
    </div>
  )
}
ProspectDetailPage function · typescript · L3-L13 (11 LOC)
src/app/(dashboard)/pipeline/[id]/page.tsx
export default function ProspectDetailPage() {
  return (
    <div className="space-y-6">
      <EmptyState
        icon="business"
        title="Prospect details"
        description="Full prospect detail view with CRM record, generated site preview, email drafts, and activity timeline will be available in Phase 4."
      />
    </div>
  )
}
PipelinePage function · typescript · L5-L32 (28 LOC)
src/app/(dashboard)/pipeline/page.tsx
export default function PipelinePage() {
  return (
    <div className="space-y-6">
      <div>
        <p className="text-sm text-gray-500">
          Track prospects as they move through discovery, enrichment, site
          generation, and outreach.
        </p>
      </div>

      <EmptyState
        icon="conversion_path"
        title="No prospects in pipeline"
        description="Discover leads to fill your pipeline. Prospects will appear here as they move through each stage."
        action={
          <Button asChild>
            <Link href="/discover">
              <span className="material-symbols-outlined text-[18px]">
                travel_explore
              </span>
              Discover leads
            </Link>
          </Button>
        }
      />
    </div>
  )
}
SettingsPage function · typescript · L31-L64 (34 LOC)
src/app/(dashboard)/settings/page.tsx
export default function SettingsPage() {
  return (
    <div className="space-y-6">
      <div>
        <p className="text-sm text-gray-500">
          Configure your pipeline, integrations, and preferences.
        </p>
      </div>

      <div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
        {SECTIONS.map((section) => (
          <Card
            key={section.title}
            className="cursor-pointer transition-shadow hover:shadow-md"
          >
            <CardHeader>
              <div className="flex items-center gap-3">
                <div className="flex h-10 w-10 items-center justify-center rounded-lg bg-gray-50">
                  <span className="material-symbols-outlined text-[20px] text-gray-400">
                    {section.icon}
                  </span>
                </div>
                <CardTitle>{section.title}</CardTitle>
              </div>
            </CardHeader>
            <CardContent>
              <p className="text-sm text-gray-500
RootLayout function · typescript · L17-L36 (20 LOC)
src/app/layout.tsx
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en" className={manrope.variable}>
      <head>
        <link
          href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,300,0,0"
          rel="stylesheet"
        />
      </head>
      <body className="font-sans antialiased">
        {children}
        <Toaster position="bottom-right" richColors />
      </body>
    </html>
  )
}
Methodology: Repobility · https://repobility.com/research/state-of-ai-code-2026/
MRRSnapshot function · typescript · L37-L78 (42 LOC)
src/components/dashboard/MRRSnapshot.tsx
export function MRRSnapshot() {
  return (
    <Card>
      <CardHeader>
        <CardTitle>Revenue snapshot</CardTitle>
      </CardHeader>
      <CardContent>
        <div className="grid grid-cols-2 gap-4">
          {METRICS.map((metric) => (
            <div key={metric.label} className="flex items-start gap-3">
              <div className="flex h-9 w-9 items-center justify-center rounded-lg bg-gray-50">
                <span className="material-symbols-outlined text-[18px] text-gray-400">
                  {metric.icon}
                </span>
              </div>
              <div>
                <p className="text-lg font-bold text-gray-900">
                  {metric.value}
                </p>
                <p className="text-xs text-gray-500">{metric.label}</p>
                {metric.change && (
                  <p
                    className={cn(
                      'mt-0.5 text-xs font-medium',
                      metric.changeType === 'positive'
             
PipelineSummary function · typescript · L14-L50 (37 LOC)
src/components/dashboard/PipelineSummary.tsx
export function PipelineSummary() {
  return (
    <div>
      <h2 className="mb-3 text-sm font-semibold text-gray-900">Pipeline overview</h2>
      <div className="grid grid-cols-2 gap-3 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-7">
        {STAGES.map((stage) => (
          <Card
            key={stage.label}
            className="flex cursor-pointer flex-col items-center px-3 py-4 transition-shadow hover:shadow-md"
          >
            <div
              className={cn(
                'flex h-10 w-10 items-center justify-center rounded-lg',
                stage.bg
              )}
            >
              <span
                className={cn(
                  'material-symbols-outlined text-[20px]',
                  stage.color
                )}
              >
                {stage.icon}
              </span>
            </div>
            <span className="mt-2 text-2xl font-bold text-gray-900">
              {stage.count}
            </span>
            <span className
QuickActions function · typescript · L27-L52 (26 LOC)
src/components/dashboard/QuickActions.tsx
export function QuickActions() {
  return (
    <div>
      <h2 className="mb-3 text-sm font-semibold text-gray-900">
        Quick actions
      </h2>
      <div className="flex flex-wrap gap-2">
        {ACTIONS.map((action) => (
          <Button key={action.label} variant={action.variant} asChild>
            <Link href={action.href} className="relative">
              <span className="material-symbols-outlined text-[18px]">
                {action.icon}
              </span>
              {action.label}
              {action.badge && (
                <span className="flex h-5 min-w-[20px] items-center justify-center rounded-full bg-primary px-1.5 text-[10px] font-semibold text-white">
                  {action.badge}
                </span>
              )}
            </Link>
          </Button>
        ))}
      </div>
    </div>
  )
}
RecentActivity function · typescript · L74-L113 (40 LOC)
src/components/dashboard/RecentActivity.tsx
export function RecentActivity() {
  return (
    <Card className="flex flex-col">
      <CardHeader>
        <CardTitle>Recent activity</CardTitle>
      </CardHeader>
      <CardContent className="flex-1">
        <div className="space-y-1">
          {MOCK_ACTIVITY.map((item) => {
            const iconConfig = ACTIVITY_ICON_MAP[item.type] ?? {
              icon: 'info',
              color: 'text-gray-400',
            }
            return (
              <button
                key={item.id}
                className="flex w-full items-start gap-3 rounded-md px-2 py-2 text-left transition-colors hover:bg-gray-50"
              >
                <span
                  className={cn(
                    'material-symbols-outlined mt-0.5 text-[18px]',
                    iconConfig.color
                  )}
                >
                  {iconConfig.icon}
                </span>
                <div className="min-w-0 flex-1">
                  <p className="text-sm text-gray
Header function · typescript · L18-L68 (51 LOC)
src/components/layout/Header.tsx
export function Header() {
  const pathname = usePathname()
  const { toggle, toggleMobile, collapsed } = useSidebar()

  const title =
    PAGE_TITLES[pathname] ??
    (pathname.startsWith('/pipeline/') ? 'Prospect Detail' :
     pathname.startsWith('/clients/') ? 'Client Detail' : 'Sitegeit')

  return (
    <header
      className={cn(
        'sticky top-0 z-30 flex h-14 items-center justify-between border-b border-gray-200 bg-white px-4 transition-all duration-200',
        collapsed ? 'lg:pl-20' : 'lg:pl-64'
      )}
    >
      <div className="flex items-center gap-3">
        {/* Mobile hamburger */}
        <button
          onClick={toggleMobile}
          className="flex h-9 w-9 items-center justify-center rounded-md text-gray-500 hover:bg-gray-100 lg:hidden"
          aria-label="Open navigation"
        >
          <span className="material-symbols-outlined text-[20px]">menu</span>
        </button>

        {/* Desktop collapse toggle */}
        <button
          onClick
MainContent function · typescript · L6-L21 (16 LOC)
src/components/layout/MainContent.tsx
export function MainContent({ children }: { children: React.ReactNode }) {
  const { collapsed } = useSidebar()

  return (
    <main
      className={cn(
        'min-h-[calc(100vh-3.5rem)] transition-all duration-200',
        collapsed ? 'lg:ml-16' : 'lg:ml-60'
      )}
    >
      <div className="mx-auto max-w-7xl px-4 py-6 sm:px-6 lg:px-8">
        {children}
      </div>
    </main>
  )
}
NotificationBell function · typescript · L69-L188 (120 LOC)
src/components/layout/NotificationBell.tsx
export function NotificationBell() {
  const [open, setOpen] = useState(false)
  const panelRef = useRef<HTMLDivElement>(null)
  const buttonRef = useRef<HTMLButtonElement>(null)

  const unreadCount = MOCK_NOTIFICATIONS.filter((n) => !n.read).length

  useEffect(() => {
    function handleClickOutside(event: MouseEvent) {
      if (
        panelRef.current &&
        !panelRef.current.contains(event.target as Node) &&
        buttonRef.current &&
        !buttonRef.current.contains(event.target as Node)
      ) {
        setOpen(false)
      }
    }
    document.addEventListener('mousedown', handleClickOutside)
    return () => document.removeEventListener('mousedown', handleClickOutside)
  }, [])

  return (
    <div className="relative">
      <button
        ref={buttonRef}
        onClick={() => setOpen(!open)}
        className="relative flex h-9 w-9 items-center justify-center rounded-md text-gray-500 hover:bg-gray-100"
        aria-label={`Notifications${unreadCount > 0 ? ` ($
handleClickOutside function · typescript · L77-L86 (10 LOC)
src/components/layout/NotificationBell.tsx
    function handleClickOutside(event: MouseEvent) {
      if (
        panelRef.current &&
        !panelRef.current.contains(event.target as Node) &&
        buttonRef.current &&
        !buttonRef.current.contains(event.target as Node)
      ) {
        setOpen(false)
      }
    }
Generated by Repobility's multi-pass static-analysis pipeline (https://repobility.com)
SidebarProvider function · typescript · L15-L49 (35 LOC)
src/components/layout/SidebarContext.tsx
export function SidebarProvider({ children }: { children: React.ReactNode }) {
  const [collapsed, setCollapsed] = useState(false)
  const [mobileOpen, setMobileOpen] = useState(false)

  useEffect(() => {
    const stored = localStorage.getItem('sidebar-collapsed')
    if (stored !== null) {
      setCollapsed(stored === 'true')
    }
  }, [])

  const toggle = useCallback(() => {
    setCollapsed((prev) => {
      const next = !prev
      localStorage.setItem('sidebar-collapsed', String(next))
      return next
    })
  }, [])

  const toggleMobile = useCallback(() => {
    setMobileOpen((prev) => !prev)
  }, [])

  const closeMobile = useCallback(() => {
    setMobileOpen(false)
  }, [])

  return (
    <SidebarContext.Provider
      value={{ collapsed, mobileOpen, toggle, toggleMobile, closeMobile }}
    >
      {children}
    </SidebarContext.Provider>
  )
}
useSidebar function · typescript · L51-L57 (7 LOC)
src/components/layout/SidebarContext.tsx
export function useSidebar() {
  const context = useContext(SidebarContext)
  if (!context) {
    throw new Error('useSidebar must be used within a SidebarProvider')
  }
  return context
}
Sidebar function · typescript · L9-L140 (132 LOC)
src/components/layout/Sidebar.tsx
export function Sidebar() {
  const pathname = usePathname()
  const { collapsed, mobileOpen, closeMobile } = useSidebar()

  const isActive = (href: string) => {
    if (href === '/') return pathname === '/'
    return pathname.startsWith(href)
  }

  return (
    <>
      {/* Mobile overlay */}
      {mobileOpen && (
        <div
          className="fixed inset-0 z-40 bg-black/50 lg:hidden"
          onClick={closeMobile}
        />
      )}

      {/* Sidebar */}
      <aside
        className={cn(
          'fixed left-0 top-0 z-50 flex h-full flex-col border-r border-gray-200 bg-white transition-all duration-200',
          // Desktop
          collapsed ? 'lg:w-16' : 'lg:w-60',
          // Mobile: slide from left
          mobileOpen ? 'w-60 translate-x-0' : '-translate-x-full',
          'lg:translate-x-0'
        )}
      >
        {/* Logo */}
        <div
          className={cn(
            'flex h-14 items-center border-b border-gray-200 px-4',
            collapsed && 'l
EmptyState function · typescript · L11-L33 (23 LOC)
src/components/shared/EmptyState.tsx
export function EmptyState({
  icon,
  title,
  description,
  action,
  className,
}: EmptyStateProps) {
  return (
    <div
      className={cn(
        'flex flex-col items-center justify-center rounded-lg border border-dashed border-gray-300 bg-white px-6 py-12 text-center',
        className
      )}
    >
      <span className="material-symbols-outlined mb-3 text-4xl text-gray-300">
        {icon}
      </span>
      <h3 className="text-sm font-semibold text-gray-900">{title}</h3>
      <p className="mt-1 max-w-sm text-sm text-gray-500">{description}</p>
      {action && <div className="mt-4">{action}</div>}
    </div>
  )
}
StatusBadge function · typescript · L23-L29 (7 LOC)
src/components/shared/StatusBadge.tsx
export function StatusBadge({ status }: { status: string }) {
  const stage = PIPELINE_STAGES.find((s) => s.key === status)
  const label = stage?.label ?? status
  const variant = STAGE_VARIANT_MAP[status] ?? 'secondary'

  return <Badge variant={variant}>{label}</Badge>
}
Badge function · typescript · L28-L32 (5 LOC)
src/components/ui/badge.tsx
function Badge({ className, variant, ...props }: BadgeProps) {
  return (
    <span className={cn(badgeVariants({ variant }), className)} {...props} />
  )
}
createClient function · typescript · L3-L8 (6 LOC)
src/lib/supabase/client.ts
export function createClient() {
  return createBrowserClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
  )
}
updateSession function · typescript · L4-L55 (52 LOC)
src/lib/supabase/middleware.ts
export async function updateSession(request: NextRequest) {
  let supabaseResponse = NextResponse.next({
    request,
  })

  const supabase = createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      cookies: {
        getAll() {
          return request.cookies.getAll()
        },
        setAll(cookiesToSet: { name: string; value: string; options: CookieOptions }[]) {
          cookiesToSet.forEach(({ name, value }) =>
            request.cookies.set(name, value)
          )
          supabaseResponse = NextResponse.next({
            request,
          })
          cookiesToSet.forEach(({ name, value, options }) =>
            supabaseResponse.cookies.set(name, value, options)
          )
        },
      },
    }
  )

  const {
    data: { user },
  } = await supabase.auth.getUser()

  // If user is not logged in and trying to access dashboard routes, redirect to login
  if (
    !user &&
    !request.nextUrl.pathnam
Repobility analyzer · published findings · https://repobility.com
createClient function · typescript · L4-L29 (26 LOC)
src/lib/supabase/server.ts
export async function createClient() {
  const cookieStore = await cookies()

  return createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      cookies: {
        getAll() {
          return cookieStore.getAll()
        },
        setAll(cookiesToSet: { name: string; value: string; options: CookieOptions }[]) {
          try {
            cookiesToSet.forEach(({ name, value, options }) =>
              cookieStore.set(name, value, options)
            )
          } catch {
            // setAll is called from Server Components where cookies
            // cannot be set. This can be safely ignored when the
            // middleware is refreshing the session.
          }
        },
      },
    }
  )
}
cn function · typescript · L4-L6 (3 LOC)
src/lib/utils/cn.ts
export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs))
}
middleware function · typescript · L4-L6 (3 LOC)
src/middleware.ts
export async function middleware(request: NextRequest) {
  return await updateSession(request)
}