import { builder } from "../builder"; import { LeaderboardEntryType, UserGamificationType, AchievementType } from "../types/index"; import { user_stats, users, user_achievements, achievements, user_points, } from "../../db/schema/index"; import { eq, desc, gt, count, isNotNull, and } from "drizzle-orm"; builder.queryField("leaderboard", (t) => t.field({ type: [LeaderboardEntryType], args: { limit: t.arg.int(), offset: t.arg.int(), }, resolve: async (_root, args, ctx) => { const limit = Math.min(args.limit || 100, 500); const offset = args.offset || 0; const entries = await ctx.db .select({ user_id: user_stats.user_id, display_name: users.artist_name, avatar: users.avatar, total_weighted_points: user_stats.total_weighted_points, total_raw_points: user_stats.total_raw_points, recordings_count: user_stats.recordings_count, playbacks_count: user_stats.playbacks_count, achievements_count: user_stats.achievements_count, }) .from(user_stats) .leftJoin(users, eq(user_stats.user_id, users.id)) .orderBy(desc(user_stats.total_weighted_points)) .limit(limit) .offset(offset); return entries.map((e: any, i: number) => ({ ...e, rank: offset + i + 1 })); }, }), ); builder.queryField("userGamification", (t) => t.field({ type: UserGamificationType, nullable: true, args: { userId: t.arg.string({ required: true }), }, resolve: async (_root, args, ctx) => { const stats = await ctx.db .select() .from(user_stats) .where(eq(user_stats.user_id, args.userId)) .limit(1); let rank = 1; if (stats[0]) { const rankResult = await ctx.db .select({ count: count() }) .from(user_stats) .where(gt(user_stats.total_weighted_points, stats[0].total_weighted_points || 0)); rank = (rankResult[0]?.count || 0) + 1; } const userAchievements = await ctx.db .select({ id: achievements.id, code: achievements.code, name: achievements.name, description: achievements.description, icon: achievements.icon, category: achievements.category, date_unlocked: user_achievements.date_unlocked, progress: user_achievements.progress, required_count: achievements.required_count, }) .from(user_achievements) .leftJoin(achievements, eq(user_achievements.achievement_id, achievements.id)) .where( and( eq(user_achievements.user_id, args.userId), isNotNull(user_achievements.date_unlocked), ), ) .orderBy(desc(user_achievements.date_unlocked)); const recentPoints = await ctx.db .select({ action: user_points.action, points: user_points.points, date_created: user_points.date_created, recording_id: user_points.recording_id, }) .from(user_points) .where(eq(user_points.user_id, args.userId)) .orderBy(desc(user_points.date_created)) .limit(10); return { stats: stats[0] ? { ...stats[0], rank } : null, achievements: userAchievements.map((a: any) => ({ ...a, date_unlocked: a.date_unlocked!, })), recent_points: recentPoints, }; }, }), ); builder.queryField("achievements", (t) => t.field({ type: [AchievementType], resolve: async (_root, _args, ctx) => { return ctx.db .select() .from(achievements) .where(eq(achievements.status, "published")) .orderBy(achievements.sort); }, }), );