feat: add primary badge selection for user profiles

Implements #49 - Allow users to select one primary badge to display
on their profile instead of showing all badges at once.

Changes:
- Add PrimaryBadge enum to Prisma schema and shared types (STAFF, MOD, VIP, DISCORD)
- Add primaryBadge field to User model and all user interfaces
- Update settings component with badge selection dropdown
- Only show badges the user actually has in the dropdown
- Update profile component to display only selected badge (or all if none selected)
- Add primaryBadge to admin profile edit modal
- Update API routes and services to handle primaryBadge
- Export PrimaryBadge enum from shared-types (not just as type)

Additional fixes:
- Fix Angular output naming: rename onEdit/onDelete to edit/delete
- Update all parent components using comment-display outputs
- Add type casting for Prisma PrimaryBadge enum to shared-types enum
This commit is contained in:
2026-02-19 20:28:23 -08:00
committed by Naomi Carrigan
parent bbc3b040d0
commit 9d965808a7
16 changed files with 155 additions and 36 deletions
+8
View File
@@ -176,6 +176,13 @@ enum MangaStatus {
RETIRED
}
enum PrimaryBadge {
STAFF
MOD
VIP
DISCORD
}
model User {
id String @id @default(auto()) @map("_id") @db.ObjectId
discordId String @unique
@@ -186,6 +193,7 @@ model User {
displayName String?
bio String?
profilePublic Boolean @default(true)
primaryBadge PrimaryBadge?
website String?
discordServer String?
bluesky String?
+4 -1
View File
@@ -5,7 +5,7 @@
*/
import { FastifyPluginAsync } from "fastify";
import { User, AuditAction, AuditCategory } from "@library/shared-types";
import { User, AuditAction, AuditCategory, PrimaryBadge } from "@library/shared-types";
import { UserService } from "../../services/user.service";
import { AuditService } from "../../services/audit.service";
import { adminGuard } from "../../middleware/admin-guard";
@@ -15,6 +15,7 @@ interface UpdateUserSettingsBody {
displayName?: string;
bio?: string;
profilePublic?: boolean;
primaryBadge?: PrimaryBadge;
website?: string;
discordServer?: string;
bluesky?: string;
@@ -31,6 +32,7 @@ interface UserProfileResponse {
avatar?: string;
bio?: string;
slug?: string;
primaryBadge?: PrimaryBadge;
website?: string;
discordServer?: string;
bluesky?: string;
@@ -144,6 +146,7 @@ const usersRoutes: FastifyPluginAsync = async (app) => {
avatar: profile.avatar,
bio: profile.bio,
slug: profile.slug,
primaryBadge: profile.primaryBadge,
website: profile.website,
discordServer: profile.discordServer,
bluesky: profile.bluesky,
+10 -1
View File
@@ -4,7 +4,7 @@
* @author Naomi Carrigan
*/
import { User } from "@library/shared-types";
import { User, PrimaryBadge } from "@library/shared-types";
import { prisma } from "../lib/prisma";
import { SuggestionStatus } from "@prisma/client";
@@ -26,6 +26,7 @@ export class UserService {
displayName: user.displayName || undefined,
bio: user.bio || undefined,
profilePublic: user.profilePublic,
primaryBadge: (user.primaryBadge as PrimaryBadge) || undefined,
website: user.website || undefined,
discordServer: user.discordServer || undefined,
bluesky: user.bluesky || undefined,
@@ -61,6 +62,7 @@ export class UserService {
displayName: user.displayName || undefined,
bio: user.bio || undefined,
profilePublic: user.profilePublic,
primaryBadge: (user.primaryBadge as PrimaryBadge) || undefined,
website: user.website || undefined,
discordServer: user.discordServer || undefined,
bluesky: user.bluesky || undefined,
@@ -93,6 +95,7 @@ export class UserService {
displayName: user.displayName || undefined,
bio: user.bio || undefined,
profilePublic: user.profilePublic,
primaryBadge: (user.primaryBadge as PrimaryBadge) || undefined,
website: user.website || undefined,
discordServer: user.discordServer || undefined,
bluesky: user.bluesky || undefined,
@@ -125,6 +128,7 @@ export class UserService {
displayName: user.displayName || undefined,
bio: user.bio || undefined,
profilePublic: user.profilePublic,
primaryBadge: (user.primaryBadge as PrimaryBadge) || undefined,
website: user.website || undefined,
discordServer: user.discordServer || undefined,
bluesky: user.bluesky || undefined,
@@ -169,6 +173,7 @@ export class UserService {
displayName: user.displayName || undefined,
bio: user.bio || undefined,
profilePublic: user.profilePublic,
primaryBadge: (user.primaryBadge as PrimaryBadge) || undefined,
website: user.website || undefined,
discordServer: user.discordServer || undefined,
bluesky: user.bluesky || undefined,
@@ -192,6 +197,7 @@ export class UserService {
displayName?: string;
bio?: string;
profilePublic?: boolean;
primaryBadge?: PrimaryBadge;
website?: string;
discordServer?: string;
bluesky?: string;
@@ -216,6 +222,7 @@ export class UserService {
displayName: user.displayName || undefined,
bio: user.bio || undefined,
profilePublic: user.profilePublic,
primaryBadge: (user.primaryBadge as PrimaryBadge) || undefined,
website: user.website || undefined,
discordServer: user.discordServer || undefined,
bluesky: user.bluesky || undefined,
@@ -239,6 +246,7 @@ export class UserService {
avatar?: string | null;
bio?: string | null;
slug?: string | null;
primaryBadge?: PrimaryBadge | null;
website?: string | null;
discordServer?: string | null;
bluesky?: string | null;
@@ -294,6 +302,7 @@ export class UserService {
avatar: user.avatar,
bio: user.bio,
slug: user.slug,
primaryBadge: user.primaryBadge as PrimaryBadge,
website: user.website,
discordServer: user.discordServer,
bluesky: user.bluesky,