"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, Settings, Package as PackageIcon, Star, Grid3x3, List, TrendingUp, Award, ChevronDown, ChevronUp } from 'lucide-react' import { RecommendedPackage, UserCategory } from '@/types/recommendations' import { Package } from '@/types' import { useLocale } from '@/contexts/LocaleContext' import { useRecommendationProfile } from '@/hooks/useRecommendationProfile' import { CATEGORY_ICONS } from '@/constants/categoryIcons' import { RecommendationCard } from './RecommendationCard' import { RecommendationListItem } from './RecommendationListItem' import { UserProfile } from '@/types/recommendations' interface RecommendationsSectionProps { onPackageToggle: (pkg: Package) => void selectedPackages: Package[] onCustomizeClick: () => void profile: UserProfile } type ViewMode = 'grid' | 'compact' type SortMode = 'recommended' | 'popularity' | 'preset' type FilterCategory = 'all' | string export function RecommendationsSection({ onPackageToggle, selectedPackages, onCustomizeClick, profile }: RecommendationsSectionProps) { const { t } = useLocale() // Derived state from props const getEffectiveOS = () => profile.selectedOS || profile.detectedOS || "ubuntu" const isProfileComplete = () => profile.categories.length > 0 && getEffectiveOS() !== "unknown" const [recommendations, setRecommendations] = useState([]) const [loading, setLoading] = useState(false) const [error, setError] = useState(null) const [viewMode, setViewMode] = useState('grid') 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, limit: 1000 }) }) 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() const recs = data.recommendations || [] setRecommendations(recs) } 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()) { // If profile just changed (e.g. from customization), we might want to force refresh // But for now, let's rely on the cache key changing which includes profile data fetchRecommendations() } }, [profile.categories, profile.selectedOS]) const isPackageSelected = (pkg: RecommendedPackage) => { return selectedPackages.some(selected => selected.id === pkg.id) } // Get package count per category const getCategoryCount = (category: string): number => { return recommendations.filter(pkg => pkg.matchedCategory === category).length } // Filter recommendations const filteredRecommendations = useMemo(() => { let result = [...recommendations] // Filter by category if (filterCategory !== 'all') { result = result.filter(pkg => pkg.matchedCategory === filterCategory) } return result }, [recommendations, filterCategory]) 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 && (
e.stopPropagation()} // Prevent collapse when clicking filter area > {/* Category Filter Tabs */}
{profile.categories.map(cat => { const count = getCategoryCount(cat) const Icon = CATEGORY_ICONS[cat] return ( ) })}
{/* 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' && (
{filteredRecommendations.map(pkg => ( ))}
)} {/* Compact View Mode */} {!loading && !error && recommendations.length > 0 && viewMode === 'compact' && (
{filteredRecommendations.map(pkg => ( ))}
)}
)}
) }