"use client"; import { Search, FileText, Package } from "lucide-react" import { Button } from "./ui/button" import { useRouter } from "../embed-shims/next-navigation" export interface EmptyStateProps { type: 'vendors' | 'posts' | 'search' | 'generic' title?: string description?: string showBackButton?: boolean onGoBack?: () => void backButtonText?: string // New CTA properties showCTA?: boolean ctaText?: string onCtaClick?: () => void /** * Link-CTA mode: render the CTA as a navigation link (`href` — plain or * `mailto:`). Wins over `onCtaClick`, and SHORT-CIRCUITS the smart-CTA * path-sniffing below (which reads `window.location.pathname` — an * SSR/client divergence server-rendered pages must never enter). */ ctaHref?: string ctaVariant?: 'primary' | 'secondary' } export function EmptyState({ type, title, description, showBackButton = false, onGoBack, backButtonText = "Go Back", showCTA = true, ctaText, onCtaClick, ctaHref, ctaVariant = 'primary' }: EmptyStateProps) { const router = useRouter() // Default content based on type const getDefaultContent = () => { switch (type) { case 'vendors': return { icon: , title: "No vendors found", description: "We couldn't find any vendors matching your criteria. Try adjusting your filters or search terms." } case 'posts': return { icon: , title: "No articles found", description: "We couldn't find any articles matching your criteria. Try different categories, tags, or search terms." } case 'search': return { icon: , title: "No results found", description: "Your search didn't return any results. Try different keywords or browse our categories." } default: return { icon: , title: "Nothing found", description: "We couldn't find what you're looking for. Try adjusting your search or filters." } } } // Smart CTA logic based on context const getSmartCTA = () => { // If custom CTA is provided, use it if (ctaText && onCtaClick) { return { text: ctaText, action: onCtaClick } } // Check if we're on the client side const isClient = typeof window !== 'undefined' const currentPath = isClient ? window.location.pathname : '' // Surgical filter reset (promoted from the hub's diverged clone during // the EmptyState convergence): delete ONLY the filter params the surface // owns — nuking the whole query string threw away unrelated state — and // use `replace` with `scroll: false` so resetting filters neither adds a // history entry nor jumps the page. const resetParams = (paramsToDelete: string[]) => { if (!isClient) return const params = new URLSearchParams(window.location.search) for (const p of paramsToDelete) params.delete(p) const queryString = params.toString() // Keep the hash — a filter reset must not clear an unrelated // deep-link anchor (e.g. #delivery-123). const hash = window.location.hash router.replace( `${window.location.pathname}${queryString ? `?${queryString}` : ''}${hash}`, { scroll: false }, ) } // Smart defaults based on type and context switch (type) { case 'search': return { text: "Reset Filters", action: () => resetParams(['search', 'page']) } case 'posts': // Blog-style listing pages (blog, case studies) reset their filters if (currentPath.includes('/blog') || currentPath.includes('/case-studies')) { return { text: "Reset Filters", action: () => resetParams(['search', 'page', 'category', 'tags']) } } else if (currentPath.includes('/profile')) { return { text: "Browse Vendors", action: () => router.push('/vendors') } } return { text: "View All Posts", action: () => router.push('/blog') } case 'vendors': // If we're in profile or other pages, direct to main content if (currentPath.includes('/profile')) { return { text: "Browse Vendors", action: () => router.push('/vendors') } } else if (currentPath.includes('/vendors') || currentPath.includes('/margin-increase/compare')) { return { text: "Reset Filters", action: () => resetParams(['search', 'page', 'category', 'subcategory']) } } return { text: "Browse Vendors", action: () => router.push('/vendors') } default: return { text: "Browse Vendors", action: () => router.push('/vendors') } } } const defaultContent = getDefaultContent() const displayTitle = title || defaultContent.title const displayDescription = description || defaultContent.description // ctaHref short-circuits BEFORE getSmartCTA(): the smart defaults read // window.location.pathname (SSR-empty, client-real), so a server-rendered // page with a link CTA must never enter that branch. Existing callers // without ctaHref keep today's smart-default behavior unchanged. const smartCTA = ctaHref ? null : getSmartCTA() const ctaClassName = ctaVariant === 'primary' ? "w-full bg-ods-accent text-ods-text-on-accent hover:bg-ods-accent-hover transition-all duration-150 font-body font-medium" : "w-full bg-transparent border border-ods-border text-ods-text-primary hover:border-ods-accent hover:text-ods-accent transition-all duration-150 font-body font-medium" return (
{/* Icon */}
{defaultContent.icon}
{/* Title */}

{displayTitle}

{/* Description */}

{displayDescription}

{/* Link CTA (ctaHref) — SSR-safe, no path sniffing */} {showCTA && ctaHref && (
)} {/* Smart CTA Button */} {showCTA && !ctaHref && smartCTA && (
)} {/* Optional Back Button */} {showBackButton && onGoBack && (
)}
) }