sync: AI review scores, reviewed page, search improvements, score filter

- Surface AI review scores across skill pages, browse, and search
- Add dedicated /reviewed page with score filtering
- Add recommended sort using Meilisearch relevance + AI scores
- Fix search sort defaulting to stars instead of recommended
- Fix CLI TypeScript null check for aiScore in search command
- Adjust cache TTL for reviewed content

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
airano
2026-03-14 05:05:37 +03:30
parent 4212b5ba47
commit 3e22d2e238
34 changed files with 929 additions and 194 deletions

View File

@@ -1,5 +1,5 @@
import Link from 'next/link';
import { Star, Download, Shield, CheckCircle, Clock, RefreshCw } from 'lucide-react';
import { Star, Download, Shield, CheckCircle, Clock, RefreshCw, Sparkles } from 'lucide-react';
import { formatCompactNumber } from '@/lib/format-number';
const FORMAT_BADGE_LABELS: Record<string, string> = {
@@ -21,6 +21,9 @@ interface SkillCardProps {
ratingCount: number | null;
securityStatus: string | null;
isVerified: boolean | null;
reviewStatus?: string | null;
aiScore?: number | null;
latestAiScore?: number | null;
sourceFormat?: string | null;
createdAt?: Date | string | null;
updatedAt?: Date | string | null;
@@ -53,6 +56,19 @@ export function SkillCard({ skill, locale, showTimeBadge, formatTimeAgo }: Skill
const showRating = (skill.ratingCount ?? 0) >= 3;
// AI review score badge (only for reviewed skills with score >= 50)
const getAiScoreBadge = () => {
const rs = skill.reviewStatus;
if (!rs || rs === 'unreviewed' || rs === 'auto-scored') return null;
const score = skill.aiScore ?? skill.latestAiScore;
if (!score || score < 50) return null;
const color = score >= 75
? 'text-success bg-success/10 border-success/20'
: 'text-gold bg-gold/10 border-gold/20';
return { score, color };
};
const aiScoreBadge = getAiScoreBadge();
return (
<Link
href={`/${locale}/skill/${skill.id}`}
@@ -101,8 +117,14 @@ export function SkillCard({ skill, locale, showTimeBadge, formatTimeAgo }: Skill
{skill.description}
</p>
{/* Metadata Row: Stars + Downloads + Rating */}
{/* Metadata Row: AI Score + Stars + Downloads + Rating */}
<div className="flex flex-wrap items-center gap-4 text-sm text-text-muted mb-2">
{aiScoreBadge && (
<span className={`flex items-center gap-1 px-1.5 py-0.5 text-xs font-medium rounded border ${aiScoreBadge.color}`}>
<Sparkles className="w-3 h-3" />
<span className="ltr-nums">{aiScoreBadge.score}</span>
</span>
)}
<span className="flex items-center gap-1">
<Star className="w-4 h-4" />
<span className="ltr-nums">{formatCompactNumber(skill.githubStars || 0, locale)}</span>