All files / src/components/features/support/FAQ index.tsx

13.09% Statements 22/168
100% Branches 0/0
0% Functions 0/1
13.09% Lines 22/168

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 1691x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x                                                                                                                                                                                                                                                                                                      
'use client';
 
/**
 * FAQ - Main FAQ/Knowledge Base component
 */
 
import React, { useState, useEffect } from 'react';
import Link from 'next/link';
import { clientLogger } from '@/lib/logging/clientLogger';
import { SupportArticle } from '@/types/support';
import { FAQ_CATEGORIES } from '@/constants/support';
import ArticleCard from './ArticleCard';
import SearchBar from './SearchBar';
import { Icon } from '@/components/ui/icons';
 
export interface FAQProps {
  /** Initial articles to display */
  initialArticles?: SupportArticle[];
  /** Active category filter */
  activeCategory?: string;
}
 
export default function FAQ({ initialArticles = [], activeCategory }: FAQProps) {
  const [articles, setArticles] = useState<SupportArticle[]>(initialArticles);
  const [searchQuery, setSearchQuery] = useState('');
  const [selectedCategory, setSelectedCategory] = useState<string | undefined>(activeCategory);
  const [isLoading, setIsLoading] = useState(false);

  // Fetch articles when category or search changes
  useEffect(() => {
    const fetchArticles = async () => {
      setIsLoading(true);
      try {
        const params = new URLSearchParams();
        if (selectedCategory) params.set('category', selectedCategory);
        if (searchQuery) params.set('search', searchQuery);

        const response = await fetch(`/api/support/articles?${params}`);
        const data = await response.json();

        if (data.success) {
          setArticles(data.data);
        }
      } catch (error) {
        clientLogger.error('Failed to fetch articles', error instanceof Error ? error : new Error(String(error)), {
          category: selectedCategory,
          searchQuery
        });
      } finally {
        setIsLoading(false);
      }
    };

    const debounceTimer = setTimeout(fetchArticles, 300);
    return () => clearTimeout(debounceTimer);
  }, [selectedCategory, searchQuery]);

  return (
    <div className="pt-[160px] sm:pt-[120px] lg:pt-[95px] xl:pt-[110px]">
      <div className="max-w-4xl mx-auto px-4 py-8">
      {/* Header */}
      <div className="text-center mb-8">
        <h1 className="text-3xl font-bold text-gray-900 dark:text-gray-100 mb-4">Help Center</h1>
        <p className="text-gray-600 dark:text-gray-400 mb-6">
          Find answers to frequently asked questions and helpful guides.
        </p>

        {/* Search */}
        <SearchBar value={searchQuery} onChange={setSearchQuery} />
      </div>

      {/* Categories */}
      <div className="flex flex-wrap justify-center gap-2 mb-8">
        <button
          type="button"
          onClick={() => setSelectedCategory(undefined)}
          className={`
            px-4 py-2 rounded-full text-sm font-medium transition-colors
            ${
              !selectedCategory
                ? 'bg-blue-600 text-white'
                : 'bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600'
            }
          `}
        >
          All Topics
        </button>
        {FAQ_CATEGORIES.map((category) => (
          <button
            key={category.id}
            type="button"
            onClick={() => setSelectedCategory(category.id)}
            className={`
              px-4 py-2 rounded-full text-sm font-medium transition-colors
              ${
                selectedCategory === category.id
                  ? 'bg-blue-600 text-white'
                  : 'bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600'
              }
            `}
          >
            {category.label}
          </button>
        ))}
      </div>

      {/* Loading State */}
      {isLoading && (
        <div className="space-y-4">
          {[1, 2, 3].map((i) => (
            <div key={i} className="bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700 p-6 animate-pulse">
              <div className="h-6 bg-gray-200 dark:bg-gray-700 rounded w-3/4 mb-2" />
              <div className="h-4 bg-gray-200 dark:bg-gray-700 rounded w-full mb-2" />
              <div className="h-4 bg-gray-200 dark:bg-gray-700 rounded w-2/3" />
            </div>
          ))}
        </div>
      )}

      {/* Empty State */}
      {!isLoading && articles.length === 0 && (
        <div className="text-center py-12">
          <div className="w-16 h-16 mx-auto mb-4 bg-gray-100 dark:bg-gray-700 rounded-full flex items-center justify-center">
            <Icon name="sad-face-circle" size={32} className="text-gray-400" />
          </div>
          <h3 className="text-lg font-medium text-gray-900 dark:text-gray-100 mb-2">No articles found</h3>
          <p className="text-gray-500 dark:text-gray-400 mb-6">
            {searchQuery
              ? `No results for "${searchQuery}"`
              : 'No articles available in this category.'}
          </p>
          <Link
            href="/support/tickets/new"
            className="inline-flex items-center gap-2 px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700"
          >
            Contact Support
          </Link>
        </div>
      )}

      {/* Articles Grid */}
      {!isLoading && articles.length > 0 && (
        <div className="space-y-4">
          {articles.map((article) => (
            <ArticleCard key={article.id} article={article} />
          ))}
        </div>
      )}

      {/* CTA */}
      <div className="mt-12 text-center p-6 bg-gray-50 dark:bg-gray-800 rounded-lg">
        <h3 className="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-2">
          Can&apos;t find what you&apos;re looking for?
        </h3>
        <p className="text-gray-600 dark:text-gray-400 mb-4">
          Our support team is here to help.
        </p>
        <Link
          href="/support/tickets/new"
          className="inline-flex items-center gap-2 px-6 py-3 bg-blue-600 text-white rounded-lg font-medium hover:bg-blue-700"
        >
          Contact Support
        </Link>
      </div>
      </div>
    </div>
  );
}