mirror of
https://github.com/yusufipk/RepoHub.git
synced 2026-09-11 10:36:07 +00:00
Remove recommendation score and reason from UI and simplify logic
This commit is contained in:
@@ -41,32 +41,7 @@ export function RecommendationCard({ pkg, isSelected, onToggle }: Recommendation
|
|||||||
{pkg.description}
|
{pkg.description}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{/* Recommendation score and reason */}
|
|
||||||
<div className="space-y-2">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<span className="text-xs text-muted-foreground">
|
|
||||||
{t('recommendations.score')}
|
|
||||||
</span>
|
|
||||||
<div className="flex items-center gap-1">
|
|
||||||
<div className="h-2 w-16 bg-secondary rounded-full overflow-hidden">
|
|
||||||
<div
|
|
||||||
className="h-full bg-primary rounded-full transition-all"
|
|
||||||
style={{ width: `${pkg.recommendationScore}%` }}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<span className="text-xs font-semibold">{pkg.recommendationScore}%</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{pkg.recommendationReason && (
|
|
||||||
<div className="pt-2 border-t">
|
|
||||||
<p className="text-xs text-muted-foreground">
|
|
||||||
<span className="font-semibold">{t('recommendations.reason')}</span>{' '}
|
|
||||||
{pkg.recommendationReason}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
variant={isSelected ? 'default' : 'outline'}
|
variant={isSelected ? 'default' : 'outline'}
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ export function RecommendationListItem({ pkg, isSelected, onToggle }: Recommenda
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`flex items-center gap-3 p-3 rounded-lg border-2 transition-all cursor-pointer hover:shadow-md ${isSelected
|
className={`flex items-center gap-3 p-3 rounded-lg border-2 transition-all cursor-pointer hover:shadow-md ${isSelected
|
||||||
? 'border-primary bg-primary/5'
|
? 'border-primary bg-primary/5'
|
||||||
: 'border-border hover:border-primary/50'
|
: 'border-border hover:border-primary/50'
|
||||||
}`}
|
}`}
|
||||||
onClick={() => onToggle(pkg)}
|
onClick={() => onToggle(pkg)}
|
||||||
>
|
>
|
||||||
@@ -35,39 +35,21 @@ export function RecommendationListItem({ pkg, isSelected, onToggle }: Recommenda
|
|||||||
<p className="text-xs text-muted-foreground truncate">{pkg.description}</p>
|
<p className="text-xs text-muted-foreground truncate">{pkg.description}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Score Badge */}
|
|
||||||
<div className="flex items-center gap-2 flex-shrink-0">
|
|
||||||
<div className="text-right">
|
|
||||||
<div className="text-xs font-semibold text-primary">
|
|
||||||
{pkg.recommendationScore}%
|
|
||||||
</div>
|
|
||||||
<div className="text-xs text-muted-foreground">match</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Info Tooltip */}
|
|
||||||
{pkg.recommendationReason && (
|
|
||||||
<div className="relative group">
|
|
||||||
<Info className="h-4 w-4 text-muted-foreground cursor-help" />
|
|
||||||
<div className="absolute right-0 top-full mt-2 w-64 p-2 bg-popover text-popover-foreground text-xs rounded-md shadow-lg border opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none z-10">
|
|
||||||
<p className="font-semibold mb-1">Why recommended?</p>
|
|
||||||
<p>{pkg.recommendationReason}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Select Button */}
|
{/* Select Button */}
|
||||||
<Button
|
<Button
|
||||||
variant={isSelected ? 'default' : 'outline'}
|
variant={isSelected ? 'default' : 'outline'}
|
||||||
size="sm"
|
size="sm"
|
||||||
className="ml-2"
|
className="ml-2"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
onToggle(pkg)
|
onToggle(pkg)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{isSelected ? '✓' : '+'}
|
{isSelected ? '✓' : '+'}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,20 +8,10 @@ import {
|
|||||||
} from "@/types/recommendations";
|
} from "@/types/recommendations";
|
||||||
import {
|
import {
|
||||||
getPresetPackageNames,
|
getPresetPackageNames,
|
||||||
getPresetPriority,
|
|
||||||
getRecommendationReason,
|
|
||||||
RECOMMENDATION_PRESETS,
|
RECOMMENDATION_PRESETS,
|
||||||
} from "@/data/recommendationPresets";
|
} from "@/data/recommendationPresets";
|
||||||
|
|
||||||
/**
|
|
||||||
* Recommendation scoring weights
|
|
||||||
*/
|
|
||||||
const SCORING_WEIGHTS = {
|
|
||||||
CATEGORY_MATCH: 0.4,
|
|
||||||
POPULARITY: 0.3,
|
|
||||||
OS_COMPATIBILITY: 0.2,
|
|
||||||
PRESET_BOOST: 0.1,
|
|
||||||
};
|
|
||||||
|
|
||||||
export class RecommendationService {
|
export class RecommendationService {
|
||||||
/**
|
/**
|
||||||
@@ -295,6 +285,9 @@ export class RecommendationService {
|
|||||||
/**
|
/**
|
||||||
* Score a package based on multiple factors
|
* Score a package based on multiple factors
|
||||||
*/
|
*/
|
||||||
|
/**
|
||||||
|
* Score a package based on simplified factors (popularity & preset)
|
||||||
|
*/
|
||||||
private static scorePackage(
|
private static scorePackage(
|
||||||
pkg: Package,
|
pkg: Package,
|
||||||
categories: UserCategory[],
|
categories: UserCategory[],
|
||||||
@@ -303,53 +296,15 @@ export class RecommendationService {
|
|||||||
experienceLevel?: ExperienceLevel,
|
experienceLevel?: ExperienceLevel,
|
||||||
matchedCategory?: UserCategory
|
matchedCategory?: UserCategory
|
||||||
): RecommendedPackage {
|
): RecommendedPackage {
|
||||||
let score = 0;
|
|
||||||
let reason = "";
|
|
||||||
const isPresetMatch = presetPackageNames.includes(pkg.name);
|
const isPresetMatch = presetPackageNames.includes(pkg.name);
|
||||||
|
|
||||||
// 1. Category Match Score (40%)
|
// Simplified score: just use popularity score (0-100)
|
||||||
// For preset packages, this is always high
|
// Give a boost to preset packages so they appear first
|
||||||
const categoryScore = isPresetMatch ? 1.0 : 0.5;
|
let finalScore = pkg.popularity_score || 0;
|
||||||
score += categoryScore * SCORING_WEIGHTS.CATEGORY_MATCH;
|
|
||||||
|
|
||||||
// 2. Popularity Score (30%)
|
|
||||||
// Normalize popularity_score (0-100) to 0-1
|
|
||||||
const popularityScore = (pkg.popularity_score || 0) / 100;
|
|
||||||
score += popularityScore * SCORING_WEIGHTS.POPULARITY;
|
|
||||||
|
|
||||||
// 3. OS Compatibility Score (20%)
|
|
||||||
// All packages from DB should be compatible, so this is always 1.0
|
|
||||||
const osScore = 1.0;
|
|
||||||
score += osScore * SCORING_WEIGHTS.OS_COMPATIBILITY;
|
|
||||||
|
|
||||||
// 4. Preset Boost (10%)
|
|
||||||
// Extra boost for preset packages based on priority
|
|
||||||
let presetBoost = 0;
|
|
||||||
if (isPresetMatch) {
|
if (isPresetMatch) {
|
||||||
const priority = getPresetPriority(pkg.name, categories, platformId);
|
finalScore += 100; // Ensure presets are always on top
|
||||||
if (priority !== null) {
|
|
||||||
presetBoost = priority / 10; // Normalize 1-10 to 0.1-1.0
|
|
||||||
|
|
||||||
// Get recommendation reason from preset
|
|
||||||
const presetReason = getRecommendationReason(pkg.name, categories);
|
|
||||||
if (presetReason) {
|
|
||||||
reason = presetReason;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
score += presetBoost * SCORING_WEIGHTS.PRESET_BOOST;
|
|
||||||
|
|
||||||
// Default reason if not from preset
|
|
||||||
if (!reason) {
|
|
||||||
if (pkg.popularity_score && pkg.popularity_score > 70) {
|
|
||||||
reason = "Popular choice in the community";
|
|
||||||
} else {
|
|
||||||
reason = "Recommended for your selected categories";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Normalize final score to 0-100
|
|
||||||
const finalScore = Math.round(score * 100);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: pkg.id,
|
id: pkg.id,
|
||||||
@@ -361,7 +316,7 @@ export class RecommendationService {
|
|||||||
license:
|
license:
|
||||||
typeof pkg.license === "string" ? pkg.license : pkg.license?.name,
|
typeof pkg.license === "string" ? pkg.license : pkg.license?.name,
|
||||||
type: pkg.type || "cli",
|
type: pkg.type || "cli",
|
||||||
platform: pkg.platform,
|
platform: pkg.platform as any,
|
||||||
platform_id: pkg.platform_id,
|
platform_id: pkg.platform_id,
|
||||||
repository: pkg.repository || "official",
|
repository: pkg.repository || "official",
|
||||||
download_url: pkg.download_url,
|
download_url: pkg.download_url,
|
||||||
@@ -371,7 +326,7 @@ export class RecommendationService {
|
|||||||
popularity_score: pkg.popularity_score,
|
popularity_score: pkg.popularity_score,
|
||||||
tags: pkg.tags,
|
tags: pkg.tags,
|
||||||
recommendationScore: finalScore,
|
recommendationScore: finalScore,
|
||||||
recommendationReason: reason,
|
recommendationReason: "", // Removed as requested
|
||||||
presetMatch: isPresetMatch,
|
presetMatch: isPresetMatch,
|
||||||
matchedCategory: matchedCategory,
|
matchedCategory: matchedCategory,
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user