import { CollectionConfig } from "payload"; import { Award } from "@/payload-types"; import { broadcastToUser } from "@/lib/realtime/bus"; import { requirePermission, hasPermission } from "@/utils/access-control/hasPermission"; export const Profiles: CollectionConfig = { slug: "profiles", admin: { useAsTitle: "profileTitle", group: "Users", defaultColumns: ["profileTitle", "user", "rank"], }, access: { read: async ({ req }) => { return await hasPermission(req.payload, req.user, "profiles:read"); }, create: async ({ req }) => { return await hasPermission(req.payload, req.user, "profiles:create"); }, update: async ({ req }) => { return await hasPermission(req.payload, req.user, "profiles:update"); }, delete: async ({ req }) => { return await hasPermission(req.payload, req.user, "profiles:delete"); }, }, hooks: { afterChange: [ async ({ doc }) => { const userId = typeof doc.user === "object" ? doc.user?.id : doc.user; if (typeof userId === "number") { broadcastToUser("profile-update", userId, { profileId: doc.id }); } }, ], }, fields: [ { name: "user", type: "relationship", relationTo: "users", required: true, }, { name: "rank", type: "relationship", relationTo: "ranks", required: true, }, { name: "profileTitle", type: "text", virtual: "user.displayName", admin: { hidden: true, }, label: "Operator", }, { name: "dossier", label: "Personnel Dossier", type: "group", fields: [ { name: "enlistmentDate", type: "date", required: true, defaultValue: ({ user }) => user?.createdAt, }, { name: "personalExcerpt", type: "textarea", }, { name: "intelExcerpt", label: "Intelligence Record", type: "richText", access: { create: async ({ req }) => { return await hasPermission(req.payload, req.user, "profiles:intel_excerpt:update"); }, update: async ({ req }) => { return await hasPermission(req.payload, req.user, "profiles:intel_excerpt:update"); }, }, }, ], }, { name: "progression", label: "Progression & XP", type: "group", fields: [ { name: "xp", label: "Experience Points (XP)", type: "number", virtual: true, defaultValue: 0, admin: { readOnly: true, }, hooks: { // beforeChange: [({ siblingData }) => delete siblingData["xp"]], afterRead: [ async ({ req, siblingData }) => { let xpAcc = 0; const progressionData = siblingData as { awards?: number[]; scoreboard?: Record; minigames?: { flappyBird?: { xpEarned?: number | null } }; }; const awardsList = progressionData?.awards || []; const scoreboard = progressionData?.scoreboard || {}; const flappyXp = progressionData?.minigames?.flappyBird?.xpEarned ?? 0; if (awardsList.length > 0) { await req.payload .find({ collection: "awards", where: { id: { in: awardsList, }, }, }) .then((res) => { const awards: Award[] = res.docs; xpAcc += awards.reduce((prev, cur) => prev + (cur.experiencePoints ?? 0), 0); }); } Object.entries(scoreboard).forEach(([k, v]) => { if (!v) return; switch (k) { case "vehicleKills": xpAcc += v * 100; break; case "armorKills": case "airKills": xpAcc += v * 500; break; case "infantryKills": default: xpAcc += v * 10; break; } }); // Flappy Bird training sim: 1 XP per 15 pipes passed in a single run xpAcc += flappyXp; return xpAcc; }, ], }, }, { name: "awards", type: "relationship", relationTo: "awards", hasMany: true, admin: { appearance: "drawer", }, }, { name: "qualifications", type: "relationship", relationTo: "qualifications", hasMany: true, admin: { appearance: "drawer", }, }, { name: "scoreboard", type: "group", admin: { readOnly: true, }, fields: [ { name: "infantryKills", type: "number", defaultValue: 0, }, { name: "vehicleKills", type: "number", defaultValue: 0, }, { name: "armorKills", type: "number", defaultValue: 0, }, { name: "airKills", type: "number", defaultValue: 0, }, { name: "deaths", type: "number", defaultValue: 0, }, ], }, { name: "minigames", label: "Minigames", type: "group", fields: [ { name: "flappyBird", label: "Flappy Bird", type: "group", fields: [ { name: "gamesPlayed", type: "number", defaultValue: 0, }, { name: "totalPasses", label: "Total Pipes Passed", type: "number", defaultValue: 0, }, { name: "bestScore", label: "Best Run (Pipes)", type: "number", defaultValue: 0, }, { name: "xpEarned", label: "XP Earned", type: "number", defaultValue: 0, }, ], }, ], }, ], }, ], };