From 4b45216d22d9a50c04b9db3ef32cf34c17fc216a Mon Sep 17 00:00:00 2001 From: ersaayan Date: Sat, 22 Nov 2025 18:52:36 +0300 Subject: [PATCH] fix: address code review feedback - Add validation for categories and experienceLevel in GET endpoint - Fix type safety: remove 'any' types, use proper Platform type - Fix hardcoded translations in Header component - Refactor platform loading to use centralized platform list - Remove duplicate getPackageManagerForOS logic - Improve architectural consistency and DRY principles Resolves Gemini Code Assist review comments --- src/app/api/recommendations/route.ts | 42 ++++++++++++-- src/components/Header.tsx | 2 +- src/components/RepoHubApp.tsx | 85 ++++++++++++---------------- src/types/recommendations.ts | 2 +- 4 files changed, 75 insertions(+), 56 deletions(-) diff --git a/src/app/api/recommendations/route.ts b/src/app/api/recommendations/route.ts index 8892b71..dc6a50a 100644 --- a/src/app/api/recommendations/route.ts +++ b/src/app/api/recommendations/route.ts @@ -1,6 +1,6 @@ import { NextRequest, NextResponse } from "next/server"; import { RecommendationService } from "@/services/recommendationService"; -import { RecommendationRequest } from "@/types/recommendations"; +import { RecommendationRequest, UserCategory, ExperienceLevel } from "@/types/recommendations"; export async function POST(request: NextRequest) { try { @@ -151,18 +151,52 @@ export async function GET(request: NextRequest) { ); } + // Validate categories + const validCategories: UserCategory[] = [ + "development", + "design", + "multimedia", + "system-tools", + "gaming", + "productivity", + "education", + ]; + const invalidCategories = categories.filter( + (cat) => !validCategories.includes(cat as UserCategory) + ); + if (invalidCategories.length > 0) { + return NextResponse.json( + { + error: `Invalid categories: ${invalidCategories.join(", ")}`, + validCategories, + }, + { status: 400 } + ); + } + + // Validate experience level if provided + const validExperienceLevels: ExperienceLevel[] = ["beginner", "intermediate", "advanced"]; + if (experienceLevel && !validExperienceLevels.includes(experienceLevel as ExperienceLevel)) { + return NextResponse.json( + { + error: `Invalid experience_level. Must be one of: ${validExperienceLevels.join(", ")}`, + }, + { status: 400 } + ); + } + // Set default limit const parsedLimit = limit && parseInt(limit) > 0 && parseInt(limit) <= 50 ? parseInt(limit) : 20; - // Generate recommendations + // Generate recommendations with validated types const recommendations = await RecommendationService.generateRecommendations( { platform_id: platformId, - categories: categories as any, - experienceLevel: experienceLevel as any, + categories: categories as UserCategory[], + experienceLevel: experienceLevel as ExperienceLevel | undefined, limit: parsedLimit, } ); diff --git a/src/components/Header.tsx b/src/components/Header.tsx index c56bbef..f229a79 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -70,7 +70,7 @@ export function Header({ cryptomusEnabled, onResetPreferences, hasProfile }: Hea > - {locale === 'tr' ? 'Tercihler' : 'Preferences'} + {t('recommendations.customize')} )} diff --git a/src/components/RepoHubApp.tsx b/src/components/RepoHubApp.tsx index 350ff3b..85cea16 100644 --- a/src/components/RepoHubApp.tsx +++ b/src/components/RepoHubApp.tsx @@ -20,6 +20,7 @@ function RepoHubAppContent({ cryptomusEnabled }: { cryptomusEnabled: boolean }) const [selectedPlatform, setSelectedPlatform] = useState(null) const [selectedPackages, setSelectedPackages] = useState([]) const [generatedScript, setGeneratedScript] = useState(null) + const [availablePlatforms, setAvailablePlatforms] = useState([]) // Recommendation profile management const { @@ -32,6 +33,22 @@ function RepoHubAppContent({ cryptomusEnabled }: { cryptomusEnabled: boolean }) const [showOnboarding, setShowOnboarding] = useState(false) + // Load platforms on mount + useEffect(() => { + const loadPlatforms = async () => { + try { + const response = await fetch('/api/platforms') + if (response.ok) { + const platforms = await response.json() + setAvailablePlatforms(platforms) + } + } catch (error) { + console.error('Failed to load platforms:', error) + } + } + loadPlatforms() + }, []) + // Show onboarding modal on first visit useEffect(() => { if (!isProfileLoading && !hasCompletedOnboarding) { @@ -97,64 +114,29 @@ function RepoHubAppContent({ cryptomusEnabled }: { cryptomusEnabled: boolean }) } const handleGenerateScript = () => { - console.log('🔧 handleGenerateScript called') - console.log('📦 selectedPackages:', selectedPackages) - console.log('🖥️ selectedPlatform:', selectedPlatform) - console.log('✅ hasCompletedOnboarding:', hasCompletedOnboarding) - if (selectedPackages.length === 0) { - console.log('❌ No packages selected') return } - // Use selected platform, or if not selected, use the platform from recommendations profile + // Use selected platform, or if not selected, find platform from available platforms let platformToUse = selectedPlatform if (!platformToUse && hasCompletedOnboarding) { - // Get effective OS from profile and find matching platform + // Get effective OS from profile and find matching platform from loaded platforms const effectiveOS = profile.selectedOS || detectedOS - console.log('🔍 effectiveOS:', effectiveOS) - - // We need to fetch the platform data - for now, create a mock platform - // This should ideally come from the platforms list - if (effectiveOS) { - platformToUse = { - id: effectiveOS, - name: effectiveOS.charAt(0).toUpperCase() + effectiveOS.slice(1), - description: '', - icon: '', - packageManager: getPackageManagerForOS(effectiveOS) + + if (effectiveOS && availablePlatforms.length > 0) { + platformToUse = availablePlatforms.find(p => p.id === effectiveOS) || null + + if (!platformToUse) { + console.warn(`Platform not found for OS: ${effectiveOS}`) } - console.log('🎯 Created platform:', platformToUse) } } if (platformToUse) { - console.log('✨ Generating script for platform:', platformToUse) const script = generateScript(selectedPackages, platformToUse) - console.log('📝 Script generated:', script) setGeneratedScript(script) - } else { - console.log('❌ No platform available') - } - } - - // Helper function to get package manager for OS - const getPackageManagerForOS = (os: string): string => { - switch (os.toLowerCase()) { - case 'windows': - return 'winget' - case 'macos': - return 'brew' - case 'ubuntu': - case 'debian': - return 'apt' - case 'fedora': - return 'dnf' - case 'arch': - return 'pacman' - default: - return 'unknown' } } @@ -228,13 +210,16 @@ function RepoHubAppContent({ cryptomusEnabled }: { cryptomusEnabled: boolean }) p.id === generatedScript.platform) || + { + id: generatedScript.platform, + name: generatedScript.platform.charAt(0).toUpperCase() + generatedScript.platform.slice(1), + description: '', + icon: '', + packageManager: '' + } + } onClose={handleCloseScriptPreview} /> )} diff --git a/src/types/recommendations.ts b/src/types/recommendations.ts index 9296aba..d86dfe5 100644 --- a/src/types/recommendations.ts +++ b/src/types/recommendations.ts @@ -52,7 +52,7 @@ export interface RecommendedPackage { category?: string; license?: string; type: "gui" | "cli"; - platform?: string | any; + platform?: Platform; platform_id?: string; repository: "official" | "third-party" | "aur"; download_url?: string;