"use client" import { useState, useEffect, useMemo } from 'react' import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' import { Button } from '@/components/ui/button' import { Sparkles, RefreshCw, Settings, Package as PackageIcon, Star, Grid3x3, List, TrendingUp, Award, Info, ChevronDown, ChevronUp } from 'lucide-react' import { RecommendedPackage } from '@/types/recommendations' import { Package } from '@/types' import { useLocale } from '@/contexts/LocaleContext' import { useRecommendationProfile } from '@/hooks/useRecommendationProfile' import { RECOMMENDATION_PRESETS } from '@/data/recommendationPresets' interface RecommendationsSectionProps { onPackageToggle: (pkg: Package) => void selectedPackages: Package[] onCustomizeClick: () => void } type ViewMode = 'grid' | 'compact' type SortMode = 'recommended' | 'popularity' | 'preset' type FilterCategory = 'all' | string export function RecommendationsSection({ onPackageToggle, selectedPackages, onCustomizeClick }: RecommendationsSectionProps) { const { t } = useLocale() const { profile, getEffectiveOS, isProfileComplete } = useRecommendationProfile() const [recommendations, setRecommendations] = useState([]) const [loading, setLoading] = useState(false) const [error, setError] = useState(null) const [viewMode, setViewMode] = useState('grid') const [sortMode, setSortMode] = useState('recommended') const [filterCategory, setFilterCategory] = useState('all') const [isExpanded, setIsExpanded] = useState(true) const fetchRecommendations = async () => { if (!isProfileComplete()) { return } setLoading(true) setError(null) try { const response = await fetch('/api/recommendations', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ platform_id: getEffectiveOS(), categories: profile.categories, experienceLevel: profile.experienceLevel, limit: 12 }) }) if (!response.ok) { const errorData = await response.json().catch(() => ({})) if (response.status === 400) { throw new Error(errorData.error || 'Invalid request parameters') } else if (response.status === 500) { throw new Error('Server error. Please try again later.') } else if (response.status === 404) { throw new Error('Recommendation service not available') } else { throw new Error(`Request failed: ${response.statusText}`) } } const data = await response.json() setRecommendations(data.recommendations || []) } catch (err) { console.error('Error fetching recommendations:', err) // User-friendly error messages if (err instanceof TypeError && err.message.includes('fetch')) { setError('Network error. Please check your internet connection.') } else { setError(err instanceof Error ? err.message : 'Failed to load recommendations') } } finally { setLoading(false) } } // Fetch recommendations on mount and when profile changes useEffect(() => { if (isProfileComplete()) { fetchRecommendations() } }, [profile.categories, profile.selectedOS, profile.experienceLevel]) const isPackageSelected = (pkg: RecommendedPackage) => { return selectedPackages.some(selected => selected.id === pkg.id) } // Get category icon from presets const getCategoryIcon = (category: string): string => { const preset = RECOMMENDATION_PRESETS.find(p => p.category === category) return preset?.icon || '📦' } // Get package count per category const getCategoryCount = (category: string): number => { return recommendations.filter(pkg => pkg.matchedCategory === category).length } // Filter and sort recommendations const filteredAndSortedRecommendations = useMemo(() => { let result = [...recommendations] // Filter by category if (filterCategory !== 'all') { result = result.filter(pkg => pkg.matchedCategory === filterCategory) } // Sort switch (sortMode) { case 'popularity': result.sort((a, b) => (b.popularity || 0) - (a.popularity || 0)) break case 'preset': result.sort((a, b) => { if (a.presetMatch && !b.presetMatch) return -1 if (!a.presetMatch && b.presetMatch) return 1 return b.recommendationScore - a.recommendationScore }) break case 'recommended': default: result.sort((a, b) => b.recommendationScore - a.recommendationScore) break } return result }, [recommendations, filterCategory, sortMode]) if (!isProfileComplete()) { return null } return ( setIsExpanded(!isExpanded)}>
{t('recommendations.title')} {!isExpanded && recommendations.length > 0 && ( {recommendations.length} {t('recommendations.packages') || 'packages'} )}
{t('recommendations.subtitle')}
{isExpanded && ( <> )}
{/* Show user profile info */} {isExpanded && (
{getEffectiveOS()} {profile.categories.map(cat => ( {t(`categories.${cat}.name`)} ))}
)} {/* Filters and View Controls */} {isExpanded && !loading && !error && recommendations.length > 0 && (
{/* Category Filter Tabs */}
{profile.categories.map(cat => { const count = getCategoryCount(cat) return ( ) })}
{/* Sort Dropdown */}
{/* View Mode Toggle */}
)}
{isExpanded && ( {loading && (

{t('recommendations.loading')}

)} {error && (

{error}

)} {!loading && !error && recommendations.length === 0 && (

{t('recommendations.no_recommendations')}

)} {!loading && !error && recommendations.length > 0 && viewMode === 'grid' && (
{filteredAndSortedRecommendations.map(pkg => ( onPackageToggle(pkg)} > {pkg.presetMatch && (
{t('recommendations.preset_badge')}
)}

{pkg.name}

{pkg.version}

{pkg.description}

{/* Recommendation score and reason */}
{t('recommendations.score')}
{pkg.recommendationScore}%
{pkg.recommendationReason && (

{t('recommendations.reason')} {' '} {pkg.recommendationReason}

)}
))}
)} {/* Compact View Mode */} {!loading && !error && recommendations.length > 0 && viewMode === 'compact' && (
{filteredAndSortedRecommendations.map(pkg => (
onPackageToggle(pkg)} > {/* Package Icon */} {/* Package Info */}

{pkg.name}

{pkg.presetMatch && ( Essential )} {pkg.version}

{pkg.description}

{/* Score Badge */}
{pkg.recommendationScore}%
match
{/* Info Tooltip */} {pkg.recommendationReason && (

Why recommended?

{pkg.recommendationReason}

)} {/* Select Button */}
))}
)}
)}
) }