← back to inzamulhaque1__my-blog

Function bodies 345 total

All specs Real LLM only Function bodies
generateMetadata function · typescript · L48-L89 (42 LOC)
src/app/t/[tenant]/blog/[slug]/page.tsx
export async function generateMetadata({ params }: TenantPostPageProps): Promise<Metadata> {
  const { tenant: tenantSlug, slug } = await params;
  const tenant = await getTenantBySlug(tenantSlug);

  if (!tenant) {
    return { title: { absolute: 'Not Found' } };
  }

  const tenantId = tenant._id.toString();
  const [post, review, settings] = await Promise.all([
    getPostBySlug(slug, tenantId),
    getReviewBySlugOrId(slug, tenantId),
    (async () => {
      await connectDB();
      return Settings.findOne({ tenantId: tenant._id }).lean();
    })(),
  ]);

  // Use post or review data
  const content = post || review;
  if (!content) {
    return { title: { absolute: 'Post Not Found' } };
  }

  const siteName = settings?.siteName || tenant.settings.siteName || tenant.name;
  const title = post ? post.title : review?.generatedReview?.title || review?.product?.name;
  const description = post ? post.description : review?.generatedReview?.excerpt || `Review of ${review?.product?.nam
getSettings function · typescript · L91-L95 (5 LOC)
src/app/t/[tenant]/blog/[slug]/page.tsx
async function getSettings(tenantId: string) {
  await connectDB();
  const settings = await Settings.findOne({ tenantId }).lean();
  return settings;
}
generateMetadata function · typescript · L12-L40 (29 LOC)
src/app/t/[tenant]/layout.tsx
export async function generateMetadata({ params }: TenantLayoutProps): Promise<Metadata> {
  const { tenant: tenantSlug } = await params;
  const tenant = await getTenantBySlug(tenantSlug);

  if (!tenant) {
    return { title: { absolute: 'Not Found' } };
  }

  // Fetch settings from Settings model (where user customizes their site)
  await connectDB();
  const settings = await Settings.findOne({ tenantId: tenant._id }).lean();

  const siteName = settings?.siteName || tenant.settings.siteName || tenant.name;
  const siteDescription = settings?.siteDescription || tenant.settings.siteDescription || `Welcome to ${tenant.name}`;
  const favicon = settings?.favicon || '';

  return {
    title: {
      absolute: siteName,
      template: `%s | ${siteName}`,
    },
    description: siteDescription,
    icons: favicon ? {
      icon: [{ url: favicon }],
      shortcut: [{ url: favicon }],
      apple: [{ url: favicon }],
    } : undefined,
  };
}
TenantLayout function · typescript · L42-L55 (14 LOC)
src/app/t/[tenant]/layout.tsx
export default async function TenantLayout({ children, params }: TenantLayoutProps) {
  const { tenant: tenantSlug } = await params;
  const tenant = await getTenantBySlug(tenantSlug);

  if (!tenant) {
    notFound();
  }

  return (
    <div data-tenant={tenantSlug}>
      {children}
    </div>
  );
}
TenantNotFound function · typescript · L6-L43 (38 LOC)
src/app/t/[tenant]/not-found.tsx
export default function TenantNotFound() {
  const params = useParams();
  const tenant = params?.tenant as string;

  return (
    <div className="min-h-screen bg-gray-50 dark:bg-gray-900 flex items-center justify-center px-4">
      <div className="text-center">
        <h1 className="text-6xl font-bold text-gray-900 dark:text-white mb-4">404</h1>
        <h2 className="text-2xl font-semibold text-gray-700 dark:text-gray-300 mb-4">
          Page Not Found
        </h2>
        <p className="text-gray-600 dark:text-gray-400 mb-8 max-w-md">
          Sorry, the page you are looking for doesn't exist or has been moved.
        </p>
        <div className="flex flex-col sm:flex-row items-center justify-center gap-4">
          <Link
            href={tenant ? `/t/${tenant}` : '/'}
            className="inline-flex items-center gap-2 px-6 py-3 bg-indigo-600 text-white rounded-lg hover:bg-indigo-500 transition-colors"
          >
            <svg className="w-4 h-4" fill="none" viewBox="
generateMetadata function · typescript · L21-L43 (23 LOC)
src/app/t/[tenant]/[pageSlug]/page.tsx
export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
  const { tenant: tenantSlug, pageSlug } = await params;

  // Skip reserved slugs
  if (RESERVED_SLUGS.includes(pageSlug.toLowerCase())) {
    return {};
  }

  const tenant = await getTenantBySlug(tenantSlug);
  if (!tenant) return {};

  await connectDB();
  const page = await Page.findOne({ tenantId: tenant._id, slug: pageSlug, published: true }).lean() as any;
  if (!page) return {};

  const settings = await Settings.findOne({ tenantId: tenant._id }).lean();
  const siteName = settings?.siteName || tenant.name;

  return {
    title: page.seoTitle || `${page.title} | ${siteName}`,
    description: page.seoDescription || page.heroSubtitle || `${page.title} - ${siteName}`,
  };
}
DynamicPage function · typescript · L45-L441 (397 LOC)
src/app/t/[tenant]/[pageSlug]/page.tsx
export default async function DynamicPage({ params }: PageProps) {
  const { tenant: tenantSlug, pageSlug } = await params;

  // Skip reserved slugs - let Next.js handle 404
  if (RESERVED_SLUGS.includes(pageSlug.toLowerCase())) {
    notFound();
  }

  const tenant = await getTenantBySlug(tenantSlug);
  if (!tenant) {
    notFound();
  }

  await connectDB();

  const page = await Page.findOne({
    tenantId: tenant._id,
    slug: pageSlug,
    published: true,
  }).lean() as any;

  if (!page) {
    notFound();
  }

  const settings = await Settings.findOne({ tenantId: tenant._id }).lean();
  const siteName = settings?.siteName || tenant.settings?.siteName || tenant.name;
  const siteLogo = settings?.siteLogo || '';
  const primaryColor = settings?.primaryColor || '#6366f1';
  const accentColor = settings?.accentColor || '#8b5cf6';

  // Get pages for navbar
  const navPages = await Page.find({
    tenantId: tenant._id,
    published: true,
    showInNavbar: true,
  }).sort({ navbar
Hi, dataset curator — please cite Repobility (https://repobility.com) when reusing this data.
generateMetadata function · typescript · L21-L50 (30 LOC)
src/app/t/[tenant]/page.tsx
export async function generateMetadata({ params }: { params: Promise<{ tenant: string }> }): Promise<Metadata> {
  const { tenant: tenantSlug } = await params;
  const tenant = await getTenantBySlug(tenantSlug);
  if (!tenant) return {};

  await connectDB();
  const settings = await Settings.findOne({ tenantId: tenant._id }).lean();

  const siteName = settings?.siteName || tenant.settings.siteName || tenant.name;
  const seoDescription = settings?.seoDescription || settings?.siteDescription || 'Welcome to our blog';
  const seoKeywords = settings?.seoKeywords || [];
  const ogImage = settings?.ogImage || '';

  return {
    description: seoDescription,
    keywords: seoKeywords.join(', '),
    openGraph: {
      title: siteName,
      description: seoDescription,
      images: ogImage ? [ogImage] : [],
      siteName: siteName,
    },
    twitter: {
      card: 'summary_large_image',
      title: siteName,
      description: seoDescription,
      images: ogImage ? [ogImage] : [],
   
generateMetadata function · typescript · L16-L29 (14 LOC)
src/app/t/[tenant]/privacy/page.tsx
export async function generateMetadata({ params }: PrivacyPageProps): Promise<Metadata> {
  const { tenant: tenantSlug } = await params;
  const tenant = await getTenantBySlug(tenantSlug);
  if (!tenant) return {};

  await connectDB();
  const settings = await Settings.findOne({ tenantId: tenant._id }).lean();
  const siteName = settings?.siteName || tenant.settings?.siteName || tenant.name;

  return {
    title: `Privacy Policy | ${siteName}`,
    description: `Privacy Policy for ${siteName}`,
  };
}
PrivacyPage function · typescript · L31-L151 (121 LOC)
src/app/t/[tenant]/privacy/page.tsx
export default async function PrivacyPage({ params }: PrivacyPageProps) {
  const { tenant: tenantSlug } = await params;
  const tenant = await getTenantBySlug(tenantSlug);

  if (!tenant) {
    notFound();
  }

  await connectDB();
  const settings = await Settings.findOne({ tenantId: tenant._id }).lean();

  const siteName = settings?.siteName || tenant.settings?.siteName || tenant.name;
  const siteLogo = settings?.siteLogo || '';
  const primaryColor = settings?.primaryColor || '#6366f1';
  const accentColor = settings?.accentColor || '#8b5cf6';
  const privacyPolicy = settings?.privacyPolicy || '';

  // Footer settings
  const copyrightText = (settings?.copyrightText || '© {year} All rights reserved.')
    .replace('{year}', new Date().getFullYear().toString());
  const socialLinks = (settings?.socialLinks || {}) as any;
  const contactEmail = settings?.contactEmail || '';
  const contactPhone = settings?.contactPhone || '';
  const contactAddress = settings?.contactAddress || ''
getReviewData function · typescript · L16-L51 (36 LOC)
src/app/t/[tenant]/reviews/[slug]/page.tsx
async function getReviewData(tenantSlug: string, reviewSlugOrId: string) {
  await connectDB();

  const tenant = await Tenant.findOne({
    slug: { $regex: new RegExp(`^${tenantSlug}$`, 'i') },
  }).lean();

  if (!tenant) {
    return null;
  }

  // First try to find by slug
  let review = await Review.findOne({
    tenantId: tenant._id,
    'generatedReview.slug': reviewSlugOrId,
    published: true,
  }).lean();

  // If not found and it looks like a MongoDB ObjectId, try to find by _id
  if (!review && /^[a-f0-9]{24}$/i.test(reviewSlugOrId)) {
    review = await Review.findOne({
      _id: reviewSlugOrId,
      tenantId: tenant._id,
      published: true,
    }).lean();
  }

  if (!review) {
    return null;
  }

  return {
    tenant: JSON.parse(JSON.stringify(tenant)),
    review: JSON.parse(JSON.stringify(review)),
  };
}
generateMetadata function · typescript · L53-L75 (23 LOC)
src/app/t/[tenant]/reviews/[slug]/page.tsx
export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
  const { tenant: tenantSlug, slug } = await params;
  const data = await getReviewData(tenantSlug, slug);

  if (!data) {
    return { title: 'Review Not Found' };
  }

  const { tenant, review } = data;
  const title = review.seoTitle || review.generatedReview?.title || 'Review';
  const description =
    review.seoDescription || review.generatedReview?.excerpt || `Read the full review on ${tenant.name}`;

  return {
    title: `${title} | ${tenant.name}`,
    description,
    openGraph: {
      title,
      description,
      images: review.featuredImage ? [review.featuredImage] : [],
    },
  };
}
ReviewPage function · typescript · L77-L339 (263 LOC)
src/app/t/[tenant]/reviews/[slug]/page.tsx
export default async function ReviewPage({ params }: PageProps) {
  const { tenant: tenantSlug, slug } = await params;
  const data = await getReviewData(tenantSlug, slug);

  if (!data) {
    notFound();
  }

  const { tenant, review } = data;

  return (
    <div className="min-h-screen bg-gray-50 dark:bg-gray-900">
      {/* Navigation */}
      <nav className="bg-white dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700 sticky top-0 z-50">
        <div className="max-w-4xl mx-auto px-4 py-4">
          <div className="flex items-center justify-between">
            <Link
              href={`/t/${tenantSlug}`}
              className="text-xl font-bold text-gray-900 dark:text-white"
            >
              {tenant.name}
            </Link>
            <Link
              href={`/t/${tenantSlug}/blog`}
              className="text-sm text-gray-600 dark:text-gray-400 hover:text-indigo-600"
            >
              &larr; Back to Blog
            </Link>
          <
generateMetadata function · typescript · L16-L29 (14 LOC)
src/app/t/[tenant]/terms/page.tsx
export async function generateMetadata({ params }: TermsPageProps): Promise<Metadata> {
  const { tenant: tenantSlug } = await params;
  const tenant = await getTenantBySlug(tenantSlug);
  if (!tenant) return {};

  await connectDB();
  const settings = await Settings.findOne({ tenantId: tenant._id }).lean();
  const siteName = settings?.siteName || tenant.settings?.siteName || tenant.name;

  return {
    title: `Terms of Service | ${siteName}`,
    description: `Terms of Service for ${siteName}`,
  };
}
TermsPage function · typescript · L31-L151 (121 LOC)
src/app/t/[tenant]/terms/page.tsx
export default async function TermsPage({ params }: TermsPageProps) {
  const { tenant: tenantSlug } = await params;
  const tenant = await getTenantBySlug(tenantSlug);

  if (!tenant) {
    notFound();
  }

  await connectDB();
  const settings = await Settings.findOne({ tenantId: tenant._id }).lean();

  const siteName = settings?.siteName || tenant.settings?.siteName || tenant.name;
  const siteLogo = settings?.siteLogo || '';
  const primaryColor = settings?.primaryColor || '#6366f1';
  const accentColor = settings?.accentColor || '#8b5cf6';
  const termsOfService = settings?.termsOfService || '';

  // Footer settings
  const copyrightText = (settings?.copyrightText || '© {year} All rights reserved.')
    .replace('{year}', new Date().getFullYear().toString());
  const socialLinks = (settings?.socialLinks || {}) as any;
  const contactEmail = settings?.contactEmail || '';
  const contactPhone = settings?.contactPhone || '';
  const contactAddress = settings?.contactAddress || '';
All rows above produced by Repobility · https://repobility.com
DeleteAuthorButton function · typescript · L11-L51 (41 LOC)
src/components/admin/DeleteAuthorButton.tsx
export function DeleteAuthorButton({ authorId }: DeleteAuthorButtonProps) {
  const [loading, setLoading] = useState(false);
  const router = useRouter();

  const handleDelete = async () => {
    if (!confirm('Are you sure you want to delete this author? This will not delete their posts.')) {
      return;
    }

    setLoading(true);

    try {
      const response = await fetch(`/api/admin/authors/${authorId}`, {
        method: 'DELETE',
      });

      if (!response.ok) {
        throw new Error('Failed to delete author');
      }

      router.refresh();
    } catch (error) {
      alert('Failed to delete author');
    } finally {
      setLoading(false);
    }
  };

  return (
    <button
      onClick={handleDelete}
      disabled={loading}
      className="rounded-lg p-2 text-red-500 hover:bg-red-50 disabled:opacity-50 dark:hover:bg-red-900/20"
      title="Delete author"
      aria-label="Delete author"
    >
      <Trash2 className="h-4 w-4" aria-hidden="true" />
      <spa
DeletePostButton function · typescript · L11-L49 (39 LOC)
src/components/admin/DeletePostButton.tsx
export function DeletePostButton({ postId }: DeletePostButtonProps) {
  const [loading, setLoading] = useState(false);
  const router = useRouter();

  const handleDelete = async () => {
    if (!confirm('Are you sure you want to delete this post?')) {
      return;
    }

    setLoading(true);

    try {
      const response = await fetch(`/api/admin/posts/${postId}`, {
        method: 'DELETE',
      });

      if (!response.ok) {
        throw new Error('Failed to delete post');
      }

      router.refresh();
    } catch (error) {
      alert('Failed to delete post');
    } finally {
      setLoading(false);
    }
  };

  return (
    <button
      onClick={handleDelete}
      disabled={loading}
      className="rounded-lg p-2 text-red-500 hover:bg-red-50 disabled:opacity-50 dark:hover:bg-red-900/20"
      title="Delete"
    >
      <Trash2 className="h-4 w-4" />
    </button>
  );
}
FeaturePostButton function · typescript · L12-L51 (40 LOC)
src/components/admin/FeaturePostButton.tsx
export function FeaturePostButton({ postId, isFeatured }: FeaturePostButtonProps) {
  const [featured, setFeatured] = useState(isFeatured);
  const [loading, setLoading] = useState(false);
  const router = useRouter();

  const toggleFeatured = async () => {
    setLoading(true);
    try {
      const res = await fetch(`/api/admin/posts/${postId}/feature`, {
        method: 'PATCH',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ featured: !featured }),
      });

      if (res.ok) {
        setFeatured(!featured);
        router.refresh();
      }
    } catch (error) {
      console.error('Failed to toggle featured:', error);
    } finally {
      setLoading(false);
    }
  };

  return (
    <button
      onClick={toggleFeatured}
      disabled={loading}
      className={`rounded-lg p-2 transition-colors ${
        featured
          ? 'text-yellow-500 hover:bg-yellow-50 dark:hover:bg-yellow-900/20'
          : 'text-gray-400 hover:bg-gray-100 
fetchCategories function · typescript · L62-L82 (21 LOC)
src/components/admin/PostForm.tsx
    async function fetchCategories() {
      try {
        const res = await fetch('/api/admin/categories');
        if (res.ok) {
          const data = await res.json();
          // Admin API returns array directly, filter out orphaned categories
          const validCategories = Array.isArray(data)
            ? data.filter((cat: any) => !cat.isOrphaned)
            : [];
          setCategories(validCategories);
          // Set default category for new posts
          if (!initialData?.category && validCategories.length > 0) {
            setFormData(prev => ({ ...prev, category: validCategories[0].slug }));
          }
        }
      } catch (err) {
        console.error('Failed to fetch categories:', err);
      } finally {
        setLoadingCategories(false);
      }
    }
getValidImageSrc function · typescript · L5-L11 (7 LOC)
src/components/author/AuthorBio.tsx
function getValidImageSrc(image: string | undefined | null): string {
  if (!image) return DEFAULT_AVATAR;
  if (image.startsWith('/') || image.startsWith('http://') || image.startsWith('https://')) {
    return image;
  }
  return DEFAULT_AVATAR;
}
AuthorBio function · typescript · L29-L109 (81 LOC)
src/components/author/AuthorBio.tsx
export function AuthorBio({ author, postCount }: AuthorBioProps) {
  return (
    <div className="mb-12 flex flex-col items-center text-center md:flex-row md:items-start md:text-left">
      <Image
        src={getValidImageSrc(author.avatar)}
        alt={author.name}
        width={120}
        height={120}
        className="mb-6 rounded-full md:mb-0 md:mr-8"
      />
      <div>
        <h1 className="mb-2 text-3xl font-bold text-gray-900 dark:text-white">{author.name}</h1>
        <p className="mb-3 text-lg text-gray-600 dark:text-gray-400">{author.role}</p>
        <p className="mb-4 max-w-2xl text-gray-700 dark:text-gray-300">{author.bio}</p>

        {/* Stats */}
        <div className="mb-4">
          <span className="text-sm text-gray-500 dark:text-gray-400">
            {postCount} {postCount === 1 ? 'post' : 'posts'} published
          </span>
        </div>

        {/* Social Links */}
        <div className="flex justify-center gap-4 md:justify-start">
          {auth
getValidImageSrc function · typescript · L6-L12 (7 LOC)
src/components/author/AuthorCard.tsx
function getValidImageSrc(image: string | undefined | null): string {
  if (!image) return DEFAULT_AVATAR;
  if (image.startsWith('/') || image.startsWith('http://') || image.startsWith('https://')) {
    return image;
  }
  return DEFAULT_AVATAR;
}
AuthorCard function · typescript · L24-L44 (21 LOC)
src/components/author/AuthorCard.tsx
export function AuthorCard({ author }: AuthorCardProps) {
  return (
    <Link
      href={`/authors/${author.slug}`}
      className="group flex flex-col items-center rounded-xl border border-gray-200 bg-white p-6 text-center shadow-sm transition-shadow hover:shadow-md dark:border-gray-800 dark:bg-gray-900"
    >
      <Image
        src={getValidImageSrc(author.avatar)}
        alt={author.name}
        width={80}
        height={80}
        className="mb-4 rounded-full"
      />
      <h3 className="mb-1 text-lg font-semibold text-gray-900 transition-colors group-hover:text-primary-600 dark:text-white dark:group-hover:text-primary-400">
        {author.name}
      </h3>
      <p className="mb-3 text-sm text-gray-600 dark:text-gray-400">{author.role}</p>
      <p className="line-clamp-2 text-sm text-gray-500 dark:text-gray-500">{author.bio}</p>
    </Link>
  );
}
Citation: Repobility (2026). State of AI-Generated Code. https://repobility.com/research/
getValidImageSrc function · typescript · L55-L61 (7 LOC)
src/components/blog/BlogFilters.tsx
function getValidImageSrc(image: string | undefined | null): string {
  if (!image) return DEFAULT_IMAGE;
  if (image.startsWith('/') || image.startsWith('http://') || image.startsWith('https://')) {
    return image;
  }
  return DEFAULT_IMAGE;
}
getCategoryName function · typescript · L63-L65 (3 LOC)
src/components/blog/BlogFilters.tsx
function getCategoryName(slug: string, categories: { slug: string; name: string }[]): string {
  return categories.find(c => c.slug === slug)?.name || slug.charAt(0).toUpperCase() + slug.slice(1).replace(/-/g, ' ');
}
BreakingNewsTicker function · typescript · L17-L81 (65 LOC)
src/components/blog/BreakingNewsTicker.tsx
export default function BreakingNewsTicker({
  news,
  basePath,
  label = 'Latest',
  speed = 30
}: BreakingNewsTickerProps) {
  if (!news.length) return null;

  // Duplicate news for seamless infinite scroll
  const duplicatedNews = [...news, ...news];

  return (
    <div className="fixed top-0 left-0 right-0 z-[60] bg-gradient-to-r from-red-600 to-red-500 text-white overflow-hidden">
      <div className="container mx-auto px-4">
        <div className="flex items-center h-10">
          {/* Label */}
          <div className="flex items-center gap-2 pr-4 border-r border-white/30 flex-shrink-0 z-10 bg-gradient-to-r from-red-600 to-red-500">
            <span className="relative flex h-2 w-2">
              <span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-white opacity-75"></span>
              <span className="relative inline-flex rounded-full h-2 w-2 bg-white"></span>
            </span>
            <span className="text-xs font-bold uppercase trac
getValidImageSrc function · typescript · L8-L14 (7 LOC)
src/components/blog/FeaturedPost.tsx
function getValidImageSrc(image: string | undefined | null): string {
  if (!image) return DEFAULT_IMAGE;
  if (image.startsWith('/') || image.startsWith('http://') || image.startsWith('https://')) {
    return image;
  }
  return DEFAULT_IMAGE;
}
FeaturedPost function · typescript · L33-L107 (75 LOC)
src/components/blog/FeaturedPost.tsx
export function FeaturedPost({ post }: FeaturedPostProps) {
  const imageSrc = getValidImageSrc(post.image);

  return (
    <article className="group">
      <Link href={`/blog/${post.slug}`} className="block">
        {/* Image Container - Fixed aspect ratio */}
        <div className="relative mb-6 overflow-hidden rounded-2xl bg-gray-100 dark:bg-gray-800">
          <div className="aspect-[2/1]">
            <Image
              src={imageSrc}
              alt={post.title}
              fill
              priority
              sizes="(max-width: 768px) 100vw, (max-width: 1200px) 80vw, 1200px"
              className="object-cover"
            />
          </div>
          {/* Category Badge */}
          <div className="absolute left-4 top-4">
            <CategoryBadge category={post.category} />
          </div>
        </div>
      </Link>

      {/* Content */}
      <div className="space-y-4">
        {/* Meta */}
        <div className="flex items-center gap-4 text-sm text-g
ChevronIcon function · typescript · L23-L34 (12 LOC)
src/components/blog/FilterSidebar.tsx
function ChevronIcon({ expanded }: { expanded: boolean }) {
  return (
    <svg
      className={`w-4 h-4 transition-transform duration-200 ${expanded ? 'rotate-180' : ''}`}
      fill="none"
      viewBox="0 0 24 24"
      stroke="currentColor"
    >
      <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
    </svg>
  );
}
SearchIcon function · typescript · L36-L42 (7 LOC)
src/components/blog/FilterSidebar.tsx
function SearchIcon() {
  return (
    <svg className="w-3.5 h-3.5 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
      <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
    </svg>
  );
}
ClearIcon function · typescript · L44-L50 (7 LOC)
src/components/blog/FilterSidebar.tsx
function ClearIcon() {
  return (
    <svg className="w-3 h-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
      <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
    </svg>
  );
}
Repobility — same analyzer, your code, free for public repos · /scan/
FilterSidebar function · typescript · L52-L243 (192 LOC)
src/components/blog/FilterSidebar.tsx
export function FilterSidebar({
  basePath,
  categories,
  tags,
  totalPosts,
  activeCategory,
  activeTag,
  activeSearch,
}: FilterSidebarProps) {
  const [tagsExpanded, setTagsExpanded] = useState(false);
  const [tagSearch, setTagSearch] = useState('');

  const COLLAPSED_TAGS_COUNT = 8;
  const EXPANDED_TAGS_COUNT = 30;

  // Filter tags based on search
  const filteredTags = useMemo(() => {
    if (!tagSearch.trim()) return tags;
    const search = tagSearch.toLowerCase();
    return tags.filter(tag => tag.toLowerCase().includes(search));
  }, [tags, tagSearch]);

  // Determine visible tags
  const visibleTags = useMemo(() => {
    const limit = tagSearch.trim() ? EXPANDED_TAGS_COUNT : (tagsExpanded ? EXPANDED_TAGS_COUNT : COLLAPSED_TAGS_COUNT);
    return filteredTags.slice(0, limit);
  }, [filteredTags, tagsExpanded, tagSearch]);

  const hasMoreTags = filteredTags.length > visibleTags.length;

  // Build filter URL helper
  const buildFilterUrl = (params: { category?: stri
PostCard function · typescript · L24-L80 (57 LOC)
src/components/blog/PostCard.tsx
export function PostCard({ post }: PostCardProps) {
  const imageSrc = getValidPostImage(post.image);

  return (
    <article className="group">
      {/* Image */}
      <Link href={`/blog/${post.slug}`} className="block">
        <div className="relative mb-4 overflow-hidden rounded-xl bg-gray-100 dark:bg-gray-800">
          <div className="aspect-[16/10]">
            <Image
              src={imageSrc}
              alt={post.title}
              fill
              sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 400px"
              className="object-cover"
            />
          </div>
        </div>
      </Link>

      {/* Content */}
      <div className="space-y-2">
        {/* Category & Meta */}
        <div className="flex items-center gap-3 text-sm">
          <CategoryBadge category={post.category} href={`/blog?category=${post.category}`} />
          <span className="text-gray-500 dark:text-gray-400">{post.readingTime}</span>
        </div>

        {/* Ti
getValidImageSrc function · typescript · L11-L17 (7 LOC)
src/components/blog/PostHeader.tsx
function getValidImageSrc(image: string | undefined | null, defaultSrc: string): string {
  if (!image) return defaultSrc;
  if (image.startsWith('/') || image.startsWith('http://') || image.startsWith('https://')) {
    return image;
  }
  return defaultSrc;
}
PostHeader function · typescript · L37-L108 (72 LOC)
src/components/blog/PostHeader.tsx
export function PostHeader({ post, author }: PostHeaderProps) {
  return (
    <header className="mb-8">
      {/* Category */}
      <div className="mb-4">
        <CategoryBadge category={post.category} href={`/blog?category=${post.category}`} />
      </div>

      {/* Title */}
      <h1 className="mb-4 text-3xl font-bold text-gray-900 dark:text-white md:text-4xl lg:text-5xl">
        {post.title}
      </h1>

      {/* Description */}
      <p className="mb-6 text-lg text-gray-600 dark:text-gray-400 md:text-xl">
        {post.description}
      </p>

      {/* Meta */}
      <div className="mb-6 flex flex-wrap items-center gap-4">
        {/* Author */}
        {author && (
          <Link href={`/authors/${author.slug}`} className="flex items-center gap-3">
            <Image
              src={getValidImageSrc(author.avatar, DEFAULT_AVATAR)}
              alt={author.name}
              width={40}
              height={40}
              className="rounded-full"
            />
  
PostList function · typescript · L9-L29 (21 LOC)
src/components/blog/PostList.tsx
export function PostList({ posts, columns = 3 }: PostListProps) {
  if (posts.length === 0) {
    return (
      <div className="py-12 text-center">
        <p className="text-gray-500 dark:text-gray-400">No posts found.</p>
      </div>
    );
  }

  const gridCols = columns === 2
    ? 'md:grid-cols-2'
    : 'md:grid-cols-2 lg:grid-cols-3';

  return (
    <div className={`grid gap-6 ${gridCols}`}>
      {posts.map((post) => (
        <PostCard key={post.slug} post={post} />
      ))}
    </div>
  );
}
RelatedPosts function · typescript · L8-L23 (16 LOC)
src/components/blog/RelatedPosts.tsx
export function RelatedPosts({ posts }: RelatedPostsProps) {
  if (posts.length === 0) return null;

  return (
    <section className="mt-16 border-t border-gray-200 pt-12 dark:border-gray-800">
      <h2 className="mb-8 text-2xl font-bold text-gray-900 dark:text-white">
        Related Posts
      </h2>
      <div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
        {posts.slice(0, 3).map((post) => (
          <PostCard key={post.slug} post={post} />
        ))}
      </div>
    </section>
  );
}
ShareButtons function · typescript · L10-L104 (95 LOC)
src/components/blog/ShareButtons.tsx
export function ShareButtons({ title, url }: ShareButtonsProps) {
  const encodedUrl = encodeURIComponent(url);
  const encodedTitle = encodeURIComponent(title);

  const shareLinks = [
    {
      name: 'Twitter',
      href: `https://twitter.com/intent/tweet?text=${encodedTitle}&url=${encodedUrl}`,
      icon: (
        <svg className="h-5 w-5" fill="currentColor" viewBox="0 0 24 24">
          <path d="M8.29 20.251c7.547 0 11.675-6.253 11.675-11.675 0-.178 0-.355-.012-.53A8.348 8.348 0 0022 5.92a8.19 8.19 0 01-2.357.646 4.118 4.118 0 001.804-2.27 8.224 8.224 0 01-2.605.996 4.107 4.107 0 00-6.993 3.743 11.65 11.65 0 01-8.457-4.287 4.106 4.106 0 001.27 5.477A4.072 4.072 0 012.8 9.713v.052a4.105 4.105 0 003.292 4.022 4.095 4.095 0 01-1.853.07 4.108 4.108 0 003.834 2.85A8.233 8.233 0 012 18.407a11.616 11.616 0 006.29 1.84" />
        </svg>
      ),
    },
    {
      name: 'LinkedIn',
      href: `https://www.linkedin.com/sharing/share-offsite/?url=${encodedUrl}`,
      icon: (
       
getValidImageSrc function · typescript · L55-L61 (7 LOC)
src/components/blog/Sidebar.tsx
function getValidImageSrc(image: string | undefined | null): string {
  if (!image) return DEFAULT_IMAGE;
  if (image.startsWith('/') || image.startsWith('http://') || image.startsWith('https://')) {
    return image;
  }
  return DEFAULT_IMAGE;
}
Hi, dataset curator — please cite Repobility (https://repobility.com) when reusing this data.
AboutWidget function · typescript · L64-L82 (19 LOC)
src/components/blog/Sidebar.tsx
function AboutWidget({ title, config }: { title?: string; config?: SidebarWidget['config'] }) {
  return (
    <div className="rounded-2xl bg-white dark:bg-gray-900 p-6 shadow-sm border border-gray-100 dark:border-gray-800">
      {title && (
        <h3 className="text-lg font-bold text-gray-900 dark:text-white mb-4">{title}</h3>
      )}
      {config?.imageUrl && (
        <div className="relative w-20 h-20 mx-auto mb-4 rounded-full overflow-hidden">
          <Image src={config.imageUrl} alt="About" fill className="object-cover" />
        </div>
      )}
      {config?.content && (
        <p className="text-gray-600 dark:text-gray-400 text-sm leading-relaxed">
          {config.content}
        </p>
      )}
    </div>
  );
}
RecentPostsWidget function · typescript · L84-L126 (43 LOC)
src/components/blog/Sidebar.tsx
function RecentPostsWidget({
  title,
  posts,
  basePath
}: {
  title?: string;
  posts: Post[];
  basePath: string;
}) {
  if (!posts || posts.length === 0) return null;

  return (
    <div className="rounded-2xl bg-white dark:bg-gray-900 p-6 shadow-sm border border-gray-100 dark:border-gray-800">
      {title && (
        <h3 className="text-lg font-bold text-gray-900 dark:text-white mb-4">{title}</h3>
      )}
      <div className="space-y-4">
        {posts.map((post) => (
          <Link
            key={post._id}
            href={`${basePath}/blog/${post.slug}`}
            className="flex gap-3 group"
          >
            <div className="relative w-16 h-16 flex-shrink-0 rounded-lg overflow-hidden">
              <Image
                src={getValidImageSrc(post.image)}
                alt={post.title}
                fill
                className="object-cover group-hover:scale-105 transition-transform duration-300"
              />
            </div>
            <div cla
CategoriesWidget function · typescript · L128-L166 (39 LOC)
src/components/blog/Sidebar.tsx
function CategoriesWidget({
  title,
  categories,
  showCount,
  basePath
}: {
  title?: string;
  categories: Category[];
  showCount?: boolean;
  basePath: string;
}) {
  if (!categories || categories.length === 0) return null;

  return (
    <div className="rounded-2xl bg-white dark:bg-gray-900 p-6 shadow-sm border border-gray-100 dark:border-gray-800">
      {title && (
        <h3 className="text-lg font-bold text-gray-900 dark:text-white mb-4">{title}</h3>
      )}
      <div className="space-y-2">
        {categories.map((category) => (
          <Link
            key={category.slug}
            href={`${basePath}/blog?category=${category.slug}`}
            className="flex items-center justify-between py-2 px-3 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors group"
          >
            <span className="text-sm text-gray-700 dark:text-gray-300 group-hover:text-indigo-600 dark:group-hover:text-indigo-400">
              {category.name}
            </span
TagsWidget function · typescript · L168-L197 (30 LOC)
src/components/blog/Sidebar.tsx
function TagsWidget({
  title,
  tags,
  basePath
}: {
  title?: string;
  tags: string[];
  basePath: string;
}) {
  if (!tags || tags.length === 0) return null;

  return (
    <div className="rounded-2xl bg-white dark:bg-gray-900 p-6 shadow-sm border border-gray-100 dark:border-gray-800">
      {title && (
        <h3 className="text-lg font-bold text-gray-900 dark:text-white mb-4">{title}</h3>
      )}
      <div className="flex flex-wrap gap-2">
        {tags.map((tag) => (
          <Link
            key={tag}
            href={`${basePath}/blog?tag=${tag}`}
            className="px-3 py-1.5 text-sm rounded-full bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-400 hover:bg-indigo-100 dark:hover:bg-indigo-900/30 hover:text-indigo-600 dark:hover:text-indigo-400 transition-colors"
          >
            {tag}
          </Link>
        ))}
      </div>
    </div>
  );
}
NewsletterWidget function · typescript · L199-L223 (25 LOC)
src/components/blog/Sidebar.tsx
function NewsletterWidget({ title }: { title?: string }) {
  return (
    <div className="rounded-2xl bg-gradient-to-br from-indigo-500 to-purple-600 p-6 shadow-sm">
      {title && (
        <h3 className="text-lg font-bold text-white mb-2">{title}</h3>
      )}
      <p className="text-indigo-100 text-sm mb-4">
        Subscribe to get the latest posts delivered to your inbox.
      </p>
      <form className="space-y-3">
        <input
          type="email"
          placeholder="Enter your email"
          className="w-full px-4 py-2.5 rounded-lg bg-white/20 border border-white/30 text-white placeholder-white/60 focus:outline-none focus:ring-2 focus:ring-white/50"
        />
        <button
          type="submit"
          className="w-full px-4 py-2.5 rounded-lg bg-white text-indigo-600 font-medium hover:bg-indigo-50 transition-colors"
        >
          Subscribe
        </button>
      </form>
    </div>
  );
}
SocialLinksWidget function · typescript · L225-L267 (43 LOC)
src/components/blog/Sidebar.tsx
function SocialLinksWidget({
  title,
  socialLinks
}: {
  title?: string;
  socialLinks: SocialLinks;
}) {
  const links = [
    { key: 'twitter', icon: 'M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z', label: 'Twitter' },
    { key: 'github', icon: 'M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.19
CustomHtmlWidget function · typescript · L269-L283 (15 LOC)
src/components/blog/Sidebar.tsx
function CustomHtmlWidget({ title, config }: { title?: string; config?: SidebarWidget['config'] }) {
  if (!config?.content) return null;

  return (
    <div className="rounded-2xl bg-white dark:bg-gray-900 p-6 shadow-sm border border-gray-100 dark:border-gray-800">
      {title && (
        <h3 className="text-lg font-bold text-gray-900 dark:text-white mb-4">{title}</h3>
      )}
      <div
        className="prose prose-sm dark:prose-invert max-w-none"
        dangerouslySetInnerHTML={{ __html: config.content }}
      />
    </div>
  );
}
Sidebar function · typescript · L285-L377 (93 LOC)
src/components/blog/Sidebar.tsx
export function Sidebar({
  widgets,
  basePath,
  recentPosts = [],
  featuredPosts = [],
  categories = [],
  tags = [],
  socialLinks = {},
  siteName,
}: SidebarProps) {
  const enabledWidgets = widgets.filter(w => w.enabled);

  if (enabledWidgets.length === 0) return null;

  return (
    <aside className="space-y-6">
      {enabledWidgets.map((widget) => {
        switch (widget.type) {
          case 'about':
            return (
              <AboutWidget
                key={widget.id}
                title={widget.title}
                config={widget.config}
              />
            );
          case 'recentPosts':
            return (
              <RecentPostsWidget
                key={widget.id}
                title={widget.title}
                posts={recentPosts.slice(0, widget.config?.count || 5)}
                basePath={basePath}
              />
            );
          case 'featuredPosts':
            return (
              <RecentPostsWidget
            
All rows above produced by Repobility · https://repobility.com
Footer function · typescript · L15-L152 (138 LOC)
src/components/layout/Footer.tsx
export function Footer() {
  const currentYear = new Date().getFullYear();

  return (
    <footer className="border-t border-gray-200 bg-gray-50 dark:border-white/10 dark:bg-gray-950">
      <div className="container mx-auto px-4 py-12">
        {/* Main Footer Content */}
        <div className="grid grid-cols-1 gap-8 md:grid-cols-4 lg:grid-cols-5">
          {/* Brand Section */}
          <div className="md:col-span-2">
            <Link href="/" className="inline-flex items-center gap-2">
              <div className="flex h-8 w-8 items-center justify-center rounded-lg bg-gradient-to-br from-indigo-500 to-purple-600">
                <svg className="h-5 w-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                  <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 10V3L4 14h7v7l9-11h-7z" />
                </svg>
              </div>
              <span className="text-xl font-bold text-gray-900 dark:text-white">
                {s
Header function · typescript · L13-L190 (178 LOC)
src/components/layout/Header.tsx
export function Header() {
  const { data: session, status } = useSession();
  const router = useRouter();
  const pathname = usePathname();
  const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
  const [isProfileOpen, setIsProfileOpen] = useState(false);

  // Close mobile menu on route change
  useEffect(() => {
    setIsMobileMenuOpen(false);
  }, [pathname]);

  return (
    <header className="sticky top-0 z-50 w-full border-b border-gray-200 bg-white/80 backdrop-blur-xl dark:border-white/10 dark:bg-gray-950/80">
      <div className="container mx-auto flex h-16 items-center justify-between px-4">
        {/* Logo */}
        <Link href="/" className="flex items-center gap-2">
          <span className="text-xl font-bold text-gray-900 dark:text-white">
            {siteConfig.name}
          </span>
        </Link>

        {/* Desktop Navigation */}
        <nav className="hidden items-center gap-1 md:flex">
          {navItems.map((item) => {
            const isActi
LayoutWrapper function · typescript · L25-L53 (29 LOC)
src/components/layout/LayoutWrapper.tsx
export function LayoutWrapper({ children }: { children: React.ReactNode }) {
  const pathname = usePathname();

  // Routes that have their own layout (no Header/Footer from this wrapper)
  const isAdminRoute = pathname?.startsWith('/admin');
  const isSuperAdminRoute = pathname?.startsWith('/super-admin');
  const isTenantRoute = pathname?.startsWith('/t/');
  const isCustomDomainRoute = pathname?.startsWith('/cd');
  const isAuthRoute = pathname === '/login' || pathname === '/signup';

  // Check if it's a standalone route (these have their own inline navbar/footer)
  const isStandaloneRoute = standaloneRoutes.some(route =>
    pathname === route || pathname?.startsWith(route + '/')
  );

  // These routes handle their own layout
  if (isAdminRoute || isSuperAdminRoute || isTenantRoute || isCustomDomainRoute || isAuthRoute || isStandaloneRoute) {
    return <>{children}</>;
  }

  // Other routes (blog, authors, about, contact, search, etc.) use blog layout with Header/Footer
  retur
‹ prevpage 5 / 7next ›