Files
skillhub/apps/web/components/ReviewedSortSelector.tsx
airano 3e22d2e238 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>
2026-03-14 05:05:37 +03:30

71 lines
2.5 KiB
TypeScript

'use client';
import { useRouter, useSearchParams } from 'next/navigation';
import { useTransition } from 'react';
interface ReviewedSortSelectorProps {
locale: string;
translations: {
sortBy: string;
reviewDate: string;
aiScore: string;
scoreFilter: string;
scoreAbove50: string;
scoreAll: string;
};
}
export function ReviewedSortSelector({ locale, translations }: ReviewedSortSelectorProps) {
const router = useRouter();
const searchParams = useSearchParams();
const [isPending, startTransition] = useTransition();
const currentSort = searchParams.get('sort') || 'reviewDate';
const currentScore = searchParams.get('score') || '50';
const navigate = (updates: Record<string, string>) => {
const params = new URLSearchParams(searchParams.toString());
for (const [key, value] of Object.entries(updates)) {
params.set(key, value);
}
params.delete('page');
startTransition(() => {
router.push(`/${locale}/reviewed?${params.toString()}`);
});
};
return (
<div className="flex items-center gap-4 flex-wrap">
<div className="flex items-center gap-2">
<label htmlFor="sort-select" className="text-sm text-text-secondary whitespace-nowrap">
{translations.sortBy}
</label>
<select
id="sort-select"
value={currentSort}
onChange={(e) => navigate({ sort: e.target.value })}
disabled={isPending}
className="text-sm border border-border rounded-lg px-3 py-1.5 bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-primary-500 disabled:opacity-50"
>
<option value="reviewDate">{translations.reviewDate}</option>
<option value="aiScore">{translations.aiScore}</option>
</select>
</div>
<div className="flex items-center gap-2">
<label htmlFor="score-filter" className="text-sm text-text-secondary whitespace-nowrap">
{translations.scoreFilter}
</label>
<select
id="score-filter"
value={currentScore}
onChange={(e) => navigate({ score: e.target.value })}
disabled={isPending}
className="text-sm border border-border rounded-lg px-3 py-1.5 bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-primary-500 disabled:opacity-50"
>
<option value="50">{translations.scoreAbove50}</option>
<option value="0">{translations.scoreAll}</option>
</select>
</div>
</div>
);
}