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 { builder } from "../builder";
|
||||||
import { ArticleType, ArticleListType, AdminArticleListType } from "../types/index";
|
import { ArticleType, ArticleListType, AdminArticleListType } from "../types/index";
|
||||||
import { articles, users } from "../../db/schema/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 { requireAdmin } from "../../lib/acl";
|
||||||
import type { DB } from "../../db/connection";
|
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) =>
|
builder.queryField("article", (t) =>
|
||||||
t.field({
|
t.field({
|
||||||
type: ArticleType,
|
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`
|
const ARTICLE_BY_SLUG_QUERY = gql`
|
||||||
query GetArticleBySlug($slug: String!) {
|
query GetArticleBySlug($slug: String!) {
|
||||||
article(slug: $slug) {
|
article(slug: $slug) {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { getArticles } from "$lib/services";
|
import { getArticles, getArticleCategories } from "$lib/services";
|
||||||
|
|
||||||
const LIMIT = 24;
|
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 page = Math.max(1, parseInt(url.searchParams.get("page") || "1", 10));
|
||||||
const offset = (page - 1) * LIMIT;
|
const offset = (page - 1) * LIMIT;
|
||||||
|
|
||||||
const result = await getArticles({ search, sortBy: sort, category, offset, limit: LIMIT }, fetch);
|
const [result, categories] = await Promise.all([
|
||||||
return { ...result, search, sort, category, page, limit: LIMIT };
|
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"}
|
value={data.category ?? "all"}
|
||||||
options={[
|
options={[
|
||||||
{ value: "all", label: $_("magazine.categories.all") },
|
{ value: "all", label: $_("magazine.categories.all") },
|
||||||
{ value: "photography", label: $_("magazine.categories.photography") },
|
...data.categories.map((c) => ({
|
||||||
{ value: "production", label: $_("magazine.categories.production") },
|
value: c,
|
||||||
{ value: "interview", label: $_("magazine.categories.interview") },
|
label: $_(`magazine.categories.${c}`, { default: c }),
|
||||||
{ value: "psychology", label: $_("magazine.categories.psychology") },
|
})),
|
||||||
{ value: "trends", label: $_("magazine.categories.trends") },
|
|
||||||
{ value: "spotlight", label: $_("magazine.categories.spotlight") },
|
|
||||||
]}
|
]}
|
||||||
onchange={(v) => setParam("category", v)}
|
onchange={(v) => setParam("category", v)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
Reference in New Issue
Block a user