feat(members): update roleEnum to support OWNER, MODERATOR, MEMBER, and GUEST
All checks were successful
Deploy Waveform to VPS / deploy (push) Successful in 2m39s
All checks were successful
Deploy Waveform to VPS / deploy (push) Successful in 2m39s
This commit is contained in:
parent
009c1ef9de
commit
8ab022eabe
3 changed files with 32 additions and 30 deletions
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
"use client";
|
||||
|
||||
import { useState, useRef, useEffect } from "react";
|
||||
import { useState, useRef, useEffect, ReactNode } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { clsx } from "clsx";
|
||||
import { UserAvatar } from "../ui/UserAvatar";
|
||||
|
|
@ -16,16 +16,11 @@ import {
|
|||
} 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: ServerMemberWithUser;
|
||||
isOffline?: boolean;
|
||||
}
|
||||
import { Crown, ShieldCheck } from "lucide-react";
|
||||
import { Member } from "@/db/schema";
|
||||
|
||||
/** Renders an individual member item with an interactive user profile popover and role badge. */
|
||||
export function MemberItem({ member, isOffline = false }: MemberItemProps) {
|
||||
export function MemberItem({ member }: { member: ServerMemberWithUser }) {
|
||||
const router = useRouter();
|
||||
const { setActiveServer } = useActiveServer();
|
||||
const { closeMembers } = useSidebarStore();
|
||||
|
|
@ -37,7 +32,13 @@ export function MemberItem({ member, isOffline = false }: MemberItemProps) {
|
|||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const triggerRef = useRef<HTMLButtonElement>(null);
|
||||
|
||||
// Close popover on click outside or ESC key press
|
||||
/** Map of server member roles to their corresponding icon elements. */
|
||||
const ROLE_ICONS: Partial<Record<Member["role"], ReactNode>> = {
|
||||
OWNER: <Crown className="w-4 h-4 text-amber-400" />,
|
||||
MODERATOR: <ShieldCheck className="w-4 h-4 text-cyan-400" />,
|
||||
};
|
||||
|
||||
/** Closes popover on click outside or ESC key press. */
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
if (
|
||||
|
|
@ -125,26 +126,26 @@ export function MemberItem({ member, isOffline = false }: MemberItemProps) {
|
|||
onClick={() => setIsProfileOpen((prev) => !prev)}
|
||||
className={clsx(
|
||||
"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.status == "OFFLINE" && "opacity-50",
|
||||
)}
|
||||
>
|
||||
<div className="flex items-center gap-2.5 min-w-0">
|
||||
<div className="relative shrink-0">
|
||||
<UserAvatar user={member} size="md" />
|
||||
</div>
|
||||
<span className="text-sm font-medium text-muted group-hover:text-white truncate">
|
||||
{member.username}
|
||||
</span>
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-sm font-semibold text-foreground truncate leading-tight group-hover:text-accent transition-colors">
|
||||
{member.username}
|
||||
</p>
|
||||
<p className="text-xs text-muted truncate leading-tight font-medium capitalize">
|
||||
{member.role.toLowerCase()}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Rolle-Badge/Icon */}
|
||||
<div className="shrink-0 ml-1">
|
||||
{member.role === "OWNER" && (
|
||||
<Crown className="w-4 h-4 text-amber-400" />
|
||||
)}
|
||||
{member.role === "ADMIN" && (
|
||||
<Shield className="w-4 h-4 text-indigo-400" />
|
||||
)}
|
||||
{/* Role Badges & Icons */}
|
||||
<div className="shrink-0 ml-1 flex items-center gap-1">
|
||||
{ROLE_ICONS[member.role]}
|
||||
</div>
|
||||
</button>
|
||||
|
||||
|
|
|
|||
|
|
@ -8,11 +8,7 @@ import { MemberItem } from "@/components/members/MemberItem";
|
|||
import { ServerMemberWithUser } from "@/lib/context/ServerContext";
|
||||
|
||||
/** Renders online and offline community members in distinct sections. */
|
||||
export function MemberList({
|
||||
members = [],
|
||||
}: {
|
||||
members?: ServerMemberWithUser[];
|
||||
}) {
|
||||
export function MemberList({ members }: { members: ServerMemberWithUser[] }) {
|
||||
const { onlineMembers, offlineMembers } = useMemo(() => {
|
||||
return {
|
||||
onlineMembers: members.filter((m) => m.status !== "OFFLINE"),
|
||||
|
|
@ -46,7 +42,7 @@ export function MemberList({
|
|||
</h2>
|
||||
<div className="space-y-0.5">
|
||||
{offlineMembers.map((member) => (
|
||||
<MemberItem key={member.id} member={member} isOffline />
|
||||
<MemberItem key={member.id} member={member} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -19,7 +19,12 @@ import {
|
|||
// ==========================================
|
||||
|
||||
/** Enum representing member access roles within a server. */
|
||||
export const roleEnum = pgEnum("role", ["OWNER", "ADMIN", "MEMBER"]);
|
||||
export const roleEnum = pgEnum("role", [
|
||||
"OWNER",
|
||||
"MODERATOR",
|
||||
"MEMBER",
|
||||
"GUEST",
|
||||
]);
|
||||
|
||||
/** Enum representing online status of a user. */
|
||||
export const userStatusEnum = pgEnum("user_status", [
|
||||
|
|
@ -67,7 +72,7 @@ export const servers = pgTable("servers", {
|
|||
/** Database table definition linking users to servers with specific roles. */
|
||||
export const members = pgTable("members", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
role: roleEnum("role").default("MEMBER").notNull(),
|
||||
role: roleEnum("role").default("GUEST").notNull(),
|
||||
userId: uuid("user_id")
|
||||
.references(() => users.id, { onDelete: "cascade" })
|
||||
.notNull(),
|
||||
|
|
|
|||
Loading…
Reference in a new issue