feat: dynamic magazine category filter from published articles
Add articleCategories GraphQL query returning distinct categories from published articles; magazine page fetches them at load time and renders only categories that actually have content. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { builder } from "../builder";
|
||||
import { ArticleType, ArticleListType, AdminArticleListType } from "../types/index";
|
||||
import { articles, users } from "../../db/schema/index";
|
||||
import { eq, and, lte, desc, asc, ilike, or, count, arrayContains, type SQL } from "drizzle-orm";
|
||||
import { eq, and, lte, desc, asc, ilike, or, count, arrayContains, isNotNull, type SQL } from "drizzle-orm";
|
||||
import { requireAdmin } from "../../lib/acl";
|
||||
import type { DB } from "../../db/connection";
|
||||
|
||||
@@ -74,6 +74,20 @@ builder.queryField("articles", (t) =>
|
||||
}),
|
||||
);
|
||||
|
||||
builder.queryField("articleCategories", (t) =>
|
||||
t.field({
|
||||
type: ["String"],
|
||||
resolve: async (_root, _args, ctx) => {
|
||||
const rows = await ctx.db
|
||||
.selectDistinct({ category: articles.category })
|
||||
.from(articles)
|
||||
.where(and(lte(articles.publish_date, new Date()), isNotNull(articles.category)))
|
||||
.orderBy(asc(articles.category));
|
||||
return rows.map((r) => r.category!);
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
builder.queryField("article", (t) =>
|
||||
t.field({
|
||||
type: ArticleType,
|
||||
|
||||
@@ -298,6 +298,23 @@ export async function getArticles(
|
||||
});
|
||||
}
|
||||
|
||||
const ARTICLE_CATEGORIES_QUERY = gql`
|
||||
query GetArticleCategories {
|
||||
articleCategories
|
||||
}
|
||||
`;
|
||||
|
||||
export async function getArticleCategories(
|
||||
fetchFn?: typeof globalThis.fetch,
|
||||
): Promise<string[]> {
|
||||
return loggedApiCall("getArticleCategories", async () => {
|
||||
const data = await getGraphQLClient(fetchFn).request<{ articleCategories: string[] }>(
|
||||
ARTICLE_CATEGORIES_QUERY,
|
||||
);
|
||||
return data.articleCategories;
|
||||
});
|
||||
}
|
||||
|
||||
const ARTICLE_BY_SLUG_QUERY = gql`
|
||||
query GetArticleBySlug($slug: String!) {
|
||||
article(slug: $slug) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getArticles } from "$lib/services";
|
||||
import { getArticles, getArticleCategories } from "$lib/services";
|
||||
|
||||
const LIMIT = 24;
|
||||
|
||||
@@ -9,6 +9,9 @@ export async function load({ fetch, url }) {
|
||||
const page = Math.max(1, parseInt(url.searchParams.get("page") || "1", 10));
|
||||
const offset = (page - 1) * LIMIT;
|
||||
|
||||
const result = await getArticles({ search, sortBy: sort, category, offset, limit: LIMIT }, fetch);
|
||||
return { ...result, search, sort, category, page, limit: LIMIT };
|
||||
const [result, categories] = await Promise.all([
|
||||
getArticles({ search, sortBy: sort, category, offset, limit: LIMIT }, fetch),
|
||||
getArticleCategories(fetch),
|
||||
]);
|
||||
return { ...result, search, sort, category, page, limit: LIMIT, categories };
|
||||
}
|
||||
|
||||
@@ -82,12 +82,10 @@
|
||||
value={data.category ?? "all"}
|
||||
options={[
|
||||
{ value: "all", label: $_("magazine.categories.all") },
|
||||
{ value: "photography", label: $_("magazine.categories.photography") },
|
||||
{ value: "production", label: $_("magazine.categories.production") },
|
||||
{ value: "interview", label: $_("magazine.categories.interview") },
|
||||
{ value: "psychology", label: $_("magazine.categories.psychology") },
|
||||
{ value: "trends", label: $_("magazine.categories.trends") },
|
||||
{ value: "spotlight", label: $_("magazine.categories.spotlight") },
|
||||
...data.categories.map((c) => ({
|
||||
value: c,
|
||||
label: $_(`magazine.categories.${c}`, { default: c }),
|
||||
})),
|
||||
]}
|
||||
onchange={(v) => setParam("category", v)}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user