diff --git a/components/members/MemberItem.tsx b/components/members/MemberItem.tsx
index 3b826b2..9e9bc55 100644
--- a/components/members/MemberItem.tsx
+++ b/components/members/MemberItem.tsx
@@ -1,27 +1,30 @@
/**
* @file components/members/MemberItem.tsx
- * @description Member item component rendering user details with popover toggle for profile views and direct actions.
+ * @description Member item component rendering user details with role badge and popover toggle.
*/
"use client";
import { useState, useRef, useEffect } from "react";
import { useRouter } from "next/navigation";
-import type { User } from "@/db/schema";
import { clsx } from "clsx";
import { UserAvatar } from "../ui/UserAvatar";
import { UserProfilePopover } from "../ui/UserProfilePopover";
-import { useActiveServer } from "@/lib/context/ServerContext";
+import {
+ ServerMemberWithUser,
+ useActiveServer,
+} from "@/lib/context/ServerContext";
import { useSidebarStore } from "@/lib/stores/useSidebarStore";
import { useUser } from "@/lib/context/UserContext";
+import { Crown, Shield } from "lucide-react";
/** Props for the MemberItem component. */
interface MemberItemProps {
- member: User;
+ member: ServerMemberWithUser;
isOffline?: boolean;
}
-/** Renders an individual member item with an interactive user profile popover. */
+/** Renders an individual member item with an interactive user profile popover and role badge. */
export function MemberItem({ member, isOffline = false }: MemberItemProps) {
const router = useRouter();
const { setActiveServer } = useActiveServer();
@@ -121,16 +124,28 @@ export function MemberItem({ member, isOffline = false }: MemberItemProps) {
type="button"
onClick={() => setIsProfileOpen((prev) => !prev)}
className={clsx(
- "w-full flex items-center gap-2.5 px-2 py-1.5 rounded-md hover:bg-background/50 transition-colors cursor-pointer group text-left focus:outline-none",
+ "w-full flex items-center justify-between px-2 py-1.5 rounded-md hover:bg-background/50 transition-colors cursor-pointer group text-left focus:outline-none",
isOffline && "opacity-60",
)}
>
-
-
+
+
+
+
+
+ {member.username}
+
+
+
+ {/* Rolle-Badge/Icon */}
+
+ {member.role === "OWNER" && (
+
+ )}
+ {member.role === "ADMIN" && (
+
+ )}
-
- {member.username}
-
{isProfileOpen && (
diff --git a/components/members/MemberList.tsx b/components/members/MemberList.tsx
index dbc42ca..ce5ceb7 100644
--- a/components/members/MemberList.tsx
+++ b/components/members/MemberList.tsx
@@ -5,14 +5,14 @@
import { useMemo } from "react";
import { MemberItem } from "@/components/members/MemberItem";
-import { User } from "@/db/schema";
-
-interface MemberListProps {
- members: User[];
-}
+import { ServerMemberWithUser } from "@/lib/context/ServerContext";
/** Renders online and offline community members in distinct sections. */
-export function MemberList({ members = [] }: MemberListProps) {
+export function MemberList({
+ members = [],
+}: {
+ members?: ServerMemberWithUser[];
+}) {
const { onlineMembers, offlineMembers } = useMemo(() => {
return {
onlineMembers: members.filter((m) => m.status !== "OFFLINE"),
diff --git a/lib/context/ServerContext.tsx b/lib/context/ServerContext.tsx
index ace1f3f..79c2338 100644
--- a/lib/context/ServerContext.tsx
+++ b/lib/context/ServerContext.tsx
@@ -8,14 +8,19 @@
import { createContext, useContext, useState } from "react";
import type { Server, Channel, Category, User, Member } from "@/db/schema";
-/** Extended server type containing associated channels and categories. */
+/** Member type combining User details with server Role. */
+export type ServerMemberWithUser = User & {
+ role: Member["role"];
+};
+
+/** Server type including associated channels, categories, and members. */
export type ServerWithChannels = Server & {
channels: Channel[];
categories: Category[];
- members: User[];
+ members: ServerMemberWithUser[];
};
-/** Member representation within a server context. */
+/** Simplified member representation within server context. */
export interface ServerMember {
id: string;
name: string;
diff --git a/lib/services/server.service.ts b/lib/services/server.service.ts
index 9948134..475c29a 100644
--- a/lib/services/server.service.ts
+++ b/lib/services/server.service.ts
@@ -10,7 +10,7 @@ import { z } from "zod";
const uuidSchema = z.uuid();
-/** Retrieves a server along with its channels, categories and members sorted chronologically if the specified user is a verified member. */
+/** Retrieves a server along with channels, categories, and members (with role). */
export async function getUserServer(serverId: string, userId: string) {
if (
!uuidSchema.safeParse(serverId).success ||
@@ -47,7 +47,12 @@ export async function getUserServer(serverId: string, userId: string) {
return {
...server,
- members: server.members.map((m) => m.user).filter(Boolean),
+ members: server.members
+ .filter((m) => Boolean(m.user))
+ .map((m) => ({
+ ...m.user,
+ role: m.role,
+ })),
};
} catch (error) {
console.error(`Error fetching server ${serverId}:`, error);
@@ -55,7 +60,7 @@ export async function getUserServer(serverId: string, userId: string) {
}
}
-/** Fetches all servers that the specified user belongs to, including each server's sorted channels, categories, and members. */
+/** Fetches all servers for a user with channels, categories, and members (with role). */
export async function getUserServers(userId: string) {
if (!uuidSchema.safeParse(userId).success) {
return [];
@@ -88,7 +93,12 @@ export async function getUserServers(userId: string) {
if (!membership.server) return null;
return {
...membership.server,
- members: membership.server.members.map((m) => m.user).filter(Boolean),
+ members: membership.server.members
+ .filter((m) => Boolean(m.user))
+ .map((m) => ({
+ ...m.user,
+ role: m.role,
+ })),
};
})
.filter((server): server is NonNullable
=>