From b6afaaf67e64889542f19980079ce003bff03364 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yusuf=20=C4=B0pek?= Date: Sun, 23 Nov 2025 13:01:01 +0300 Subject: [PATCH] Refactor recommendations: fix modal closing, update icons, extract components --- src/components/OnboardingModal.tsx | 48 +++--- src/components/RecommendationCard.tsx | 85 ++++++++++ src/components/RecommendationListItem.tsx | 73 ++++++++ src/components/RecommendationsSection.tsx | 197 ++++++---------------- src/constants/categoryIcons.ts | 21 +++ src/data/recommendationPresets.ts | 7 - src/types/recommendations.ts | 2 +- 7 files changed, 257 insertions(+), 176 deletions(-) create mode 100644 src/components/RecommendationCard.tsx create mode 100644 src/components/RecommendationListItem.tsx create mode 100644 src/constants/categoryIcons.ts diff --git a/src/components/OnboardingModal.tsx b/src/components/OnboardingModal.tsx index c8c19cf..01ca110 100644 --- a/src/components/OnboardingModal.tsx +++ b/src/components/OnboardingModal.tsx @@ -8,6 +8,7 @@ import { X, ChevronRight, ChevronLeft, Sparkles } from 'lucide-react' import { UserCategory, ExperienceLevel } from '@/types/recommendations' import { useLocale } from '@/contexts/LocaleContext' import { RECOMMENDATION_PRESETS } from '@/data/recommendationPresets' +import { CATEGORY_ICONS } from '@/constants/categoryIcons' interface OnboardingModalProps { isOpen: boolean @@ -159,28 +160,33 @@ export function OnboardingModal({
- {RECOMMENDATION_PRESETS.map(preset => ( -
- - ))} + + ) + })} {selectedCategories.length > 0 && ( diff --git a/src/components/RecommendationCard.tsx b/src/components/RecommendationCard.tsx new file mode 100644 index 0000000..1876610 --- /dev/null +++ b/src/components/RecommendationCard.tsx @@ -0,0 +1,85 @@ +import { Package as PackageIcon, Star } from 'lucide-react' +import { Button } from '@/components/ui/button' +import { Card, CardContent } from '@/components/ui/card' +import { RecommendedPackage } from '@/types/recommendations' +import { useLocale } from '@/contexts/LocaleContext' + +interface RecommendationCardProps { + pkg: RecommendedPackage + isSelected: boolean + onToggle: (pkg: RecommendedPackage) => void +} + +export function RecommendationCard({ pkg, isSelected, onToggle }: RecommendationCardProps) { + const { t } = useLocale() + + return ( + onToggle(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} +

+
+ )} +
+ + + + + ) +} diff --git a/src/components/RecommendationListItem.tsx b/src/components/RecommendationListItem.tsx new file mode 100644 index 0000000..e4d1ac8 --- /dev/null +++ b/src/components/RecommendationListItem.tsx @@ -0,0 +1,73 @@ +import { Package as PackageIcon, Star, Info } from 'lucide-react' +import { Button } from '@/components/ui/button' +import { RecommendedPackage } from '@/types/recommendations' + +interface RecommendationListItemProps { + pkg: RecommendedPackage + isSelected: boolean + onToggle: (pkg: RecommendedPackage) => void +} + +export function RecommendationListItem({ pkg, isSelected, onToggle }: RecommendationListItemProps) { + return ( +
onToggle(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 */} + +
+
+ ) +} diff --git a/src/components/RecommendationsSection.tsx b/src/components/RecommendationsSection.tsx index a8188de..0d97c5f 100644 --- a/src/components/RecommendationsSection.tsx +++ b/src/components/RecommendationsSection.tsx @@ -3,12 +3,14 @@ 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 { Sparkles, RefreshCw, 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 { RECOMMENDATION_PRESETS } from '@/data/recommendationPresets' +import { CATEGORY_ICONS } from '@/constants/categoryIcons' +import { RecommendationCard } from './RecommendationCard' +import { RecommendationListItem } from './RecommendationListItem' interface RecommendationsSectionProps { onPackageToggle: (pkg: Package) => void @@ -98,12 +100,6 @@ export function RecommendationsSection({ 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 @@ -258,29 +254,40 @@ export function RecommendationsSection({ {/* 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 ( ) })} @@ -292,7 +299,10 @@ export function RecommendationsSection({ - - + pkg={pkg} + isSelected={isPackageSelected(pkg)} + onToggle={onPackageToggle} + /> ))}
)} @@ -453,68 +412,12 @@ export function RecommendationsSection({ {!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 */} - -
-
+ pkg={pkg} + isSelected={isPackageSelected(pkg)} + onToggle={onPackageToggle} + /> ))}
)} diff --git a/src/constants/categoryIcons.ts b/src/constants/categoryIcons.ts new file mode 100644 index 0000000..c706a33 --- /dev/null +++ b/src/constants/categoryIcons.ts @@ -0,0 +1,21 @@ +import { + Code, + Palette, + Film, + Cpu, + Gamepad2, + CheckSquare, + GraduationCap, + LucideIcon +} from 'lucide-react'; +import { UserCategory } from '@/types/recommendations'; + +export const CATEGORY_ICONS: Record = { + development: Code, + design: Palette, + multimedia: Film, + "system-tools": Cpu, + gaming: Gamepad2, + productivity: CheckSquare, + education: GraduationCap, +}; diff --git a/src/data/recommendationPresets.ts b/src/data/recommendationPresets.ts index fb36e7b..0e82e1c 100644 --- a/src/data/recommendationPresets.ts +++ b/src/data/recommendationPresets.ts @@ -8,7 +8,6 @@ export const RECOMMENDATION_PRESETS: CategoryPreset[] = [ { category: "development", description: "Essential tools for software development", - icon: "💻", packages: [ { packageName: "git", @@ -85,7 +84,6 @@ export const RECOMMENDATION_PRESETS: CategoryPreset[] = [ { category: "design", description: "Tools for graphic design, UI/UX, and creative work", - icon: "🎨", packages: [ { packageName: "gimp", @@ -127,7 +125,6 @@ export const RECOMMENDATION_PRESETS: CategoryPreset[] = [ { category: "multimedia", description: "Audio, video editing and media management tools", - icon: "🎬", packages: [ { packageName: "vlc", @@ -176,7 +173,6 @@ export const RECOMMENDATION_PRESETS: CategoryPreset[] = [ { category: "system-tools", description: "System administration, security and utilities", - icon: "⚙️", packages: [ { packageName: "htop", @@ -225,7 +221,6 @@ export const RECOMMENDATION_PRESETS: CategoryPreset[] = [ { category: "gaming", description: "Gaming platforms and related tools", - icon: "🎮", packages: [ { packageName: "steam", @@ -260,7 +255,6 @@ export const RECOMMENDATION_PRESETS: CategoryPreset[] = [ { category: "productivity", description: "Office, note-taking and productivity tools", - icon: "📝", packages: [ { packageName: "libreoffice", @@ -302,7 +296,6 @@ export const RECOMMENDATION_PRESETS: CategoryPreset[] = [ { category: "education", description: "Educational and scientific software", - icon: "🎓", packages: [ { packageName: "anki", diff --git a/src/types/recommendations.ts b/src/types/recommendations.ts index d86dfe5..4b56569 100644 --- a/src/types/recommendations.ts +++ b/src/types/recommendations.ts @@ -85,7 +85,7 @@ export interface CategoryPreset { category: UserCategory; packages: PackagePreset[]; description: string; - icon: string; + // Icon removed in favor of UI-side mapping } /**