refactor: add leveling view layer
Create leveling.view.ts with UI logic extracted from leaderboard command: - getLeaderboardEmbed() for leaderboard display (XP and Balance) - getMedalEmoji() helper for ranking medals (🥇🥈🥉) - formatLeaderEntry() helper for entry formatting with null safety Updated leaderboard.ts to use view functions instead of inline formatting.
This commit is contained in:
@@ -3,7 +3,8 @@ import { SlashCommandBuilder } from "discord.js";
|
|||||||
import { DrizzleClient } from "@/lib/DrizzleClient";
|
import { DrizzleClient } from "@/lib/DrizzleClient";
|
||||||
import { users } from "@/db/schema";
|
import { users } from "@/db/schema";
|
||||||
import { desc } from "drizzle-orm";
|
import { desc } from "drizzle-orm";
|
||||||
import { createWarningEmbed, createBaseEmbed } from "@lib/embeds";
|
import { createWarningEmbed } from "@lib/embeds";
|
||||||
|
import { getLeaderboardEmbed } from "@/modules/leveling/leveling.view";
|
||||||
|
|
||||||
export const leaderboard = createCommand({
|
export const leaderboard = createCommand({
|
||||||
data: new SlashCommandBuilder()
|
data: new SlashCommandBuilder()
|
||||||
@@ -34,13 +35,7 @@ export const leaderboard = createCommand({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const description = leaders.map((user, index) => {
|
const embed = getLeaderboardEmbed(leaders, isXp ? 'xp' : 'balance');
|
||||||
const medal = index === 0 ? "🥇" : index === 1 ? "🥈" : index === 2 ? "🥉" : `${index + 1}.`;
|
|
||||||
const value = isXp ? `Lvl ${user.level} (${user.xp} XP)` : `${user.balance} 🪙`;
|
|
||||||
return `${medal} **${user.username}** — ${value}`;
|
|
||||||
}).join("\n");
|
|
||||||
|
|
||||||
const embed = createBaseEmbed(isXp ? "🏆 XP Leaderboard" : "💰 Richest Players", description, "Gold");
|
|
||||||
|
|
||||||
await interaction.editReply({ embeds: [embed] });
|
await interaction.editReply({ embeds: [embed] });
|
||||||
}
|
}
|
||||||
|
|||||||
48
src/modules/leveling/leveling.view.ts
Normal file
48
src/modules/leveling/leveling.view.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import { EmbedBuilder } from "discord.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User data for leaderboard display
|
||||||
|
*/
|
||||||
|
interface LeaderboardUser {
|
||||||
|
username: string;
|
||||||
|
level: number | null;
|
||||||
|
xp: bigint | null;
|
||||||
|
balance: bigint | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the appropriate medal emoji for a ranking position
|
||||||
|
*/
|
||||||
|
function getMedalEmoji(index: number): string {
|
||||||
|
if (index === 0) return "🥇";
|
||||||
|
if (index === 1) return "🥈";
|
||||||
|
if (index === 2) return "🥉";
|
||||||
|
return `${index + 1}.`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats a single leaderboard entry based on type
|
||||||
|
*/
|
||||||
|
function formatLeaderEntry(user: LeaderboardUser, index: number, type: 'xp' | 'balance'): string {
|
||||||
|
const medal = getMedalEmoji(index);
|
||||||
|
const value = type === 'xp'
|
||||||
|
? `Lvl ${user.level ?? 1} (${user.xp ?? 0n} XP)`
|
||||||
|
: `${user.balance ?? 0n} 🪙`;
|
||||||
|
return `${medal} **${user.username}** — ${value}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a leaderboard embed for either XP or Balance rankings
|
||||||
|
*/
|
||||||
|
export function getLeaderboardEmbed(leaders: LeaderboardUser[], type: 'xp' | 'balance'): EmbedBuilder {
|
||||||
|
const description = leaders.map((user, index) =>
|
||||||
|
formatLeaderEntry(user, index, type)
|
||||||
|
).join("\n");
|
||||||
|
|
||||||
|
const title = type === 'xp' ? "🏆 XP Leaderboard" : "💰 Richest Players";
|
||||||
|
|
||||||
|
return new EmbedBuilder()
|
||||||
|
.setTitle(title)
|
||||||
|
.setDescription(description)
|
||||||
|
.setColor(0xFFD700); // Gold
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user