feat: add server-side pagination, search, and filtering to all collection and admin pages

- Public pages (videos, magazine, models): URL-driven search, sort, category/duration
  filters, and Prev/Next pagination (page size 24)
- Admin tables (videos, articles): search input, toggle filters, and pagination (page size 50)
- Tags page: tag filtering now done server-side via DB arrayContains query instead of
  fetching all items and filtering client-side
- Backend resolvers updated for videos, articles, models with paginated { items, total }
  responses and filter/sort/tag args

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-07 10:43:26 +01:00
parent c90c09da9a
commit 9c5dba5c90
17 changed files with 1159 additions and 496 deletions

View File

@@ -1,7 +1,7 @@
import { builder } from "../builder";
import { ModelType } from "../types/index";
import { ModelType, ModelListType } from "../types/index";
import { users, user_photos, files } from "../../db/schema/index";
import { eq, and, desc } from "drizzle-orm";
import { eq, and, desc, asc, ilike, count, arrayContains } from "drizzle-orm";
async function enrichModel(db: any, user: any) {
// Fetch photos
@@ -20,24 +20,32 @@ async function enrichModel(db: any, user: any) {
builder.queryField("models", (t) =>
t.field({
type: [ModelType],
type: ModelListType,
args: {
featured: t.arg.boolean(),
limit: t.arg.int(),
search: t.arg.string(),
offset: t.arg.int(),
sortBy: t.arg.string(),
tag: t.arg.string(),
},
resolve: async (_root, args, ctx) => {
let query = ctx.db
.select()
.from(users)
.where(eq(users.role, "model"))
.orderBy(desc(users.date_created));
const pageSize = args.limit ?? 24;
const offset = args.offset ?? 0;
if (args.limit) {
query = (query as any).limit(args.limit);
}
const conditions: any[] = [eq(users.role, "model")];
if (args.search) conditions.push(ilike(users.artist_name, `%${args.search}%`));
if (args.tag) conditions.push(arrayContains(users.tags, [args.tag]));
const modelList = await query;
return Promise.all(modelList.map((m: any) => enrichModel(ctx.db, m)));
const order = args.sortBy === "recent" ? desc(users.date_created) : asc(users.artist_name);
const where = and(...conditions);
const [modelList, totalRows] = await Promise.all([
ctx.db.select().from(users).where(where).orderBy(order).limit(pageSize).offset(offset),
ctx.db.select({ total: count() }).from(users).where(where),
]);
const items = await Promise.all(modelList.map((m: any) => enrichModel(ctx.db, m)));
return { items, total: totalRows[0]?.total ?? 0 };
},
}),
);