feat: add Discord profile dropdown menu with role display and integrate with admin and form layouts via AdminShellServer.
This commit is contained in:
+8
-26
@@ -27,43 +27,25 @@ import {
|
||||
FormControl,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from "@/components/ui/form";
|
||||
|
||||
|
||||
import { DropdownMenuAvatar, DiscordMemberProfile } from "@/components/dropdown-menu-avatar";
|
||||
|
||||
export function FormLayoutClient({
|
||||
userName,
|
||||
userImage,
|
||||
profile,
|
||||
}: {
|
||||
userName: string;
|
||||
userImage: string;
|
||||
profile?: DiscordMemberProfile | null;
|
||||
}) {
|
||||
const [imgError, setImgError] = useState(false);
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
{userImage && !imgError && (
|
||||
<Image
|
||||
src={userImage}
|
||||
alt={userName}
|
||||
width={28}
|
||||
height={28}
|
||||
className="rounded-full object-cover"
|
||||
onError={() => setImgError(true)}
|
||||
unoptimized
|
||||
/>
|
||||
)}
|
||||
<span className="text-sm text-muted-foreground">{userName}</span>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => signOut()}
|
||||
>
|
||||
<LogOut className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
<DropdownMenuAvatar
|
||||
userName={userName}
|
||||
userImage={userImage}
|
||||
profile={profile || null}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import { FormLayoutClient } from "./client";
|
||||
import LoginDialog from "@/components/login-dialog";
|
||||
import { Metadata } from "next";
|
||||
import { ThemeToggle } from "@/components/theme-toggle";
|
||||
import { getDiscordMemberProfile } from "@/app/actions/discord";
|
||||
import Link from 'next/link';
|
||||
|
||||
const siteUrl = process.env.BASE_URL;
|
||||
@@ -35,6 +36,12 @@ export default async function FormLayout({
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
const session = await getServerSession(authOptions);
|
||||
const discordId = (session?.user as any)?.discordId;
|
||||
let profile = null;
|
||||
|
||||
if (discordId) {
|
||||
profile = await getDiscordMemberProfile(discordId);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col flex-1 w-full bg-background text-foreground">
|
||||
@@ -49,6 +56,7 @@ export default async function FormLayout({
|
||||
<FormLayoutClient
|
||||
userName={session.user.name ?? ""}
|
||||
userImage={session.user.image ?? ""}
|
||||
profile={profile}
|
||||
/>
|
||||
)}
|
||||
<ThemeToggle />
|
||||
|
||||
+39
-2
@@ -7,6 +7,9 @@ import {
|
||||
CardDescription,
|
||||
} from "@/components/ui/card";
|
||||
import { HtmlDisplay } from "@/components/html-display";
|
||||
import { getServerSession } from "next-auth";
|
||||
import { authOptions } from "@/app/api/auth/[...nextauth]/route";
|
||||
import { getGuildMemberRoles } from "@/app/actions/discord";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
@@ -16,14 +19,48 @@ export default async function FormsListingPage() {
|
||||
orderBy: (f, { desc }) => [desc(f.createdAt)],
|
||||
});
|
||||
|
||||
const session = await getServerSession(authOptions);
|
||||
const discordId = (session?.user as { discordId?: string })?.discordId;
|
||||
|
||||
let userRoles: string[] = [];
|
||||
if (discordId) {
|
||||
try {
|
||||
userRoles = await getGuildMemberRoles(discordId);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
const visibleForms = formsList.filter((form) => {
|
||||
const allowedRoles = form.allowedRoles || [];
|
||||
const deniedRoles = form.deniedRoles || [];
|
||||
|
||||
if (allowedRoles.length === 0 && deniedRoles.length === 0) return true;
|
||||
|
||||
// If user is not logged in but there are role restrictions, hide it.
|
||||
if (!discordId) return false;
|
||||
|
||||
let isAllowed = allowedRoles.length === 0;
|
||||
if (allowedRoles.length > 0) {
|
||||
isAllowed = userRoles.some(r => allowedRoles.includes(r));
|
||||
}
|
||||
|
||||
let isDenied = false;
|
||||
if (deniedRoles.length > 0) {
|
||||
isDenied = userRoles.some(r => deniedRoles.includes(r));
|
||||
}
|
||||
|
||||
return isAllowed && !isDenied;
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="max-w-4xl mx-auto p-4 md:p-6 space-y-6">
|
||||
<h1 className="text-3xl font-bold tracking-tight">Form ทั้งหมด</h1>
|
||||
{formsList.length === 0 ? (
|
||||
{visibleForms.length === 0 ? (
|
||||
<p className="text-muted-foreground">ตอนนี้ยังไม่เปิดรับสัก Form</p>
|
||||
) : (
|
||||
<div className="flex flex-col gap-4">
|
||||
{formsList.map((form) => (
|
||||
{visibleForms.map((form) => (
|
||||
<Link key={form.id} href={`/form/${form.id}`}>
|
||||
<Card className="hover:bg-muted/50 transition-colors h-full flex flex-col">
|
||||
<CardHeader>
|
||||
|
||||
Reference in New Issue
Block a user