feat: Add web admin page for quest management and refactor Discord bot's quest UI to use new components.

This commit is contained in:
syntaxbullet
2026-01-15 17:21:49 +01:00
parent 9e5c6b5ac3
commit 2f73f38877
12 changed files with 552 additions and 94 deletions

View File

@@ -17,5 +17,8 @@ export const EVENTS = {
RELOAD_COMMANDS: "actions:reload_commands",
CLEAR_CACHE: "actions:clear_cache",
MAINTENANCE_MODE: "actions:maintenance_mode",
},
QUEST: {
COMPLETED: "quest:completed",
}
} as const;

View File

@@ -1,4 +1,4 @@
import { userQuests } from "@db/schema";
import { userQuests, quests } from "@db/schema";
import { eq, and } from "drizzle-orm";
import { UserError } from "@shared/lib/errors";
import { DrizzleClient } from "@shared/db/DrizzleClient";
@@ -7,6 +7,7 @@ import { levelingService } from "@shared/modules/leveling/leveling.service";
import { withTransaction } from "@/lib/db";
import type { Transaction } from "@shared/lib/types";
import { TransactionType } from "@shared/lib/constants";
import { systemEvents, EVENTS } from "@shared/lib/events";
export const questService = {
assignQuest: async (userId: string, questId: number, tx?: Transaction) => {
@@ -107,6 +108,14 @@ export const questService = {
results.xp = xp;
}
// Emit completion event for the bot to handle notifications
systemEvents.emit(EVENTS.QUEST.COMPLETED, {
userId,
questId,
quest: userQuest.quest,
rewards: results
});
return { success: true, rewards: results };
}, tx);
},
@@ -120,7 +129,7 @@ export const questService = {
});
},
getAvailableQuests: async (userId: string) => {
async getAvailableQuests(userId: string) {
const userQuestIds = (await DrizzleClient.query.userQuests.findMany({
where: eq(userQuests.userId, BigInt(userId)),
columns: {
@@ -133,5 +142,25 @@ export const questService = {
? notInArray(quests.id, userQuestIds)
: undefined
});
},
async createQuest(data: {
name: string;
description: string;
triggerEvent: string;
requirements: { target: number };
rewards: { xp: number; balance: number };
}, tx?: Transaction) {
return await withTransaction(async (txFn) => {
return await txFn.insert(quests)
.values({
name: data.name,
description: data.description,
triggerEvent: data.triggerEvent,
requirements: data.requirements,
rewards: data.rewards,
})
.returning();
}, tx);
}
};