feat(tags): support multiple comma-separated tags in prompts filtering

This commit is contained in:
Murat Kirazkaya
2026-01-11 20:57:25 +03:00
parent 839be322be
commit 4b326cf877
6 changed files with 59 additions and 34 deletions

View File

@@ -330,11 +330,17 @@ export async function GET(request: Request) {
}
if (tag) {
where.tags = {
some: {
tag: { slug: tag },
},
};
// Handle multiple tags (comma-separated)
const tagSlugs = tag.split(",").map(t => t.trim()).filter(Boolean);
if (tagSlugs.length > 0) {
where.AND = tagSlugs.map(slug => ({
tags: {
some: {
tag: { slug },
},
},
}));
}
}
if (q) {

View File

@@ -219,14 +219,21 @@ export default async function PromptsPage({ searchParams }: PromptsPageProps) {
where.categoryId = params.category;
}
// Handle tag parameter (can be comma-separated for multiple tags)
if (params.tag) {
where.tags = {
some: {
tag: {
slug: params.tag,
// Handle multiple tags (comma-separated)
const tagSlugs = params.tag.split(",").map(t => t.trim()).filter(Boolean);
if (tagSlugs.length > 0) {
where.AND = tagSlugs.map(slug => ({
tags: {
some: {
tag: {
slug,
},
},
},
},
};
}));
}
}
// Build order by clause

View File

@@ -20,7 +20,7 @@ interface InfinitePromptListProps {
type?: string;
category?: string;
categorySlug?: string;
tag?: string;
tag?: string;
sort?: string;
};
}

View File

@@ -81,6 +81,8 @@ export function PromptFilters({ categories, tags, currentFilters, aiSearchEnable
router.push("/prompts");
};
const selectedTags = currentFilters.tag ? currentFilters.tag.split(",").filter(Boolean) : [];
const hasFilters = currentFilters.q || currentFilters.type || currentFilters.category || currentFilters.tag || currentFilters.sort;
const activeFilterCount = [currentFilters.type, currentFilters.category, currentFilters.tag, currentFilters.sort && currentFilters.sort !== "newest"].filter(Boolean).length;
@@ -318,25 +320,34 @@ export function PromptFilters({ categories, tags, currentFilters, aiSearchEnable
/>
</div>
<div className="flex flex-wrap gap-1 max-h-48 overflow-y-auto">
{filteredTags.filter((tag) => tag.id && tag.slug).map((tag) => (
<button
key={tag.id}
className="px-2 py-0.5 text-[11px] rounded border transition-colors"
style={
currentFilters.tag === tag.slug
? { backgroundColor: tag.color, color: "white", borderColor: tag.color }
: { borderColor: tag.color + "40", color: tag.color }
}
onClick={() => {
if (currentFilters.tag !== tag.slug) {
analyticsSearch.filter("tag", tag.slug);
{filteredTags.filter((tag) => tag.id && tag.slug).map((tag) => {
const isSelected = selectedTags.includes(tag.slug);
return (
<button
key={tag.id}
className="px-2 py-0.5 text-[11px] rounded border transition-colors"
style={
isSelected
? { backgroundColor: tag.color, color: "white", borderColor: tag.color }
: { borderColor: tag.color + "40", color: tag.color }
}
updateFilter("tag", currentFilters.tag === tag.slug ? null : tag.slug);
}}
>
{tag.name}
</button>
))}
onClick={() => {
let newTags: string[];
if (isSelected) {
// Remove tag
newTags = selectedTags.filter(t => t !== tag.slug);
} else {
// Add tag
newTags = [...selectedTags, tag.slug];
analyticsSearch.filter("tag", tag.slug);
}
updateFilter("tag", newTags.length > 0 ? newTags.join(",") : null);
}}
>
{tag.name}
</button>
);
})}
{filteredTags.length === 0 && tagSearch && (
<span className="text-xs text-muted-foreground">{t("search.noResults")}</span>
)}

View File

@@ -76,9 +76,10 @@ Provide your review in a clear, actionable format with specific line references
}
// Inject when tag includes "code", "debug", "git"
if (filters?.tag) {
const tag = filters.tag.toLowerCase();
if (tag.includes("code") || tag.includes("debug") || tag.includes("git")) {
const tagParam = filters?.tag;
if (tagParam) {
const tagList = tagParam.toLowerCase();
if (tagList.includes("code") || tagList.includes("debug") || tagList.includes("git")) {
return true;
}
}

View File

@@ -10,7 +10,7 @@ export interface WidgetContext {
type?: string;
category?: string;
categorySlug?: string;
tag?: string;
tag?: string;
sort?: string;
};
page?: number;