page.tsx (8317B)
1 import { listBots } from "@/lib/api"; 2 import { User } from "@/lib/auth"; 3 import { cookies } from "next/headers"; 4 import SettingsLayout from "../settingsLayout"; 5 import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"; 6 import { Button } from "@/components/ui/button"; 7 import { LogOut, Settings, UserPlus } from "lucide-react"; 8 import { Label } from "@/components/ui/label"; 9 import { Input } from "@/components/ui/input"; 10 import { ScrollArea } from "@/components/ui/scroll-area"; 11 import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; 12 import { Badge } from "@/components/ui/badge"; 13 import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"; 14 15 export default async function MembersSettingsPage({ 16 searchParams, 17 }: { 18 searchParams: Promise<{ [key: string]: string | string[] | undefined }> 19 }) { 20 const teamIdParam = (await searchParams).team as string | undefined; 21 const cookieStore = await cookies() 22 const session: User = JSON.parse(cookieStore.get("d4all_session")?.value || "{}") 23 if (!session.token) { 24 return <div className="flex h-screen w-full items-center justify-center">Loading...</div> 25 } 26 const listData = await listBots(session.matrixId, session.token); 27 28 if (!listData) { 29 return <div className="flex h-screen w-full items-center justify-center">Loading...</div> 30 } 31 32 const selectedBot = listData.bots.find((team) => team.id === teamIdParam) ?? listData.bots[0]; 33 if (!selectedBot) { 34 // TODO: Redirect to register page? 35 return <div className="flex h-screen w-full items-center justify-center">Loading...</div> 36 } 37 // TODO: get teams from the API 38 // @ts-expect-error - members is not a property of the data yet 39 selectedBot.members = [] 40 41 return ( 42 <SettingsLayout currentTab={"members"} listData={listData} selectedBot={selectedBot} teamIdParam={teamIdParam}> 43 <div className="flex items-center justify-between"> 44 <h3 className="text-sm font-medium text-gray-400">Team Members</h3> 45 <Dialog> 46 <DialogTrigger asChild> 47 <Button size="sm" className="bg-purple-600 text-white hover:bg-purple-700"> 48 <UserPlus className="mr-2 h-4 w-4" /> 49 Invite Member 50 </Button> 51 </DialogTrigger> 52 <DialogContent className="bg-gray-950 border-gray-800"> 53 <DialogHeader> 54 <DialogTitle>Invite Team Member</DialogTitle> 55 <DialogDescription className="text-gray-400"> 56 Invite a Matrix user to join your moderation team 57 </DialogDescription> 58 </DialogHeader> 59 <div className="space-y-4 py-4"> 60 <div className="space-y-2"> 61 <Label htmlFor="invite-matrix-id">Matrix ID</Label> 62 <Input 63 id="invite-matrix-id" 64 placeholder="@user:matrix.org" 65 className="bg-gray-900 border-gray-800" 66 /> 67 </div> 68 <div className="space-y-2"> 69 <Label htmlFor="invite-role">Role</Label> 70 <select 71 id="invite-role" 72 className="w-full rounded-md border border-gray-800 bg-gray-900 px-3 py-2 text-sm" 73 > 74 <option value="moderator">Moderator</option> 75 <option value="viewer">Viewer (Read-only)</option> 76 </select> 77 </div> 78 </div> 79 <DialogFooter> 80 <Button 81 variant="outline" 82 className="border-gray-700 text-gray-400 hover:bg-gray-900 hover:text-gray-300" 83 > 84 Cancel 85 </Button> 86 <Button className="bg-purple-600 text-white hover:bg-purple-700"> 87 Send Invitation 88 </Button> 89 </DialogFooter> 90 </DialogContent> 91 </Dialog> 92 </div> 93 94 <ScrollArea className="h-[300px] rounded-md border border-gray-800"> 95 <div className="p-4 space-y-3"> 96 {/* @ts-expect-error - members is not yet a thing */} 97 {selectedBot.members.map((member) => ( 98 <div key={member.id} className="flex items-center justify-between p-3 rounded-md bg-gray-900"> 99 <div className="flex items-center gap-3"> 100 <Avatar className="h-10 w-10 border border-gray-800"> 101 <AvatarImage src={member.avatar || ""} /> 102 <AvatarFallback className="bg-gray-800 text-gray-400"> 103 {member.name.substring(1, 3).toUpperCase()} 104 </AvatarFallback> 105 </Avatar> 106 <div> 107 <p className="font-medium">{member.name}</p> 108 <div className="flex items-center gap-2"> 109 <Badge 110 className={ 111 member.role === "owner" 112 ? "bg-purple-900/50 text-purple-300" 113 : "bg-blue-900/50 text-blue-300" 114 } 115 > 116 {member.role} 117 </Badge> 118 <span className="text-xs text-gray-500"> 119 Joined {new Date(member.joined).toLocaleDateString()} 120 </span> 121 </div> 122 </div> 123 </div> 124 {member.role !== "owner" && ( 125 <DropdownMenu> 126 <DropdownMenuTrigger asChild> 127 <Button variant="ghost" size="sm" className="h-8 w-8 p-0"> 128 <span className="sr-only">Open menu</span> 129 <Settings className="h-4 w-4" /> 130 </Button> 131 </DropdownMenuTrigger> 132 <DropdownMenuContent align="end" className="bg-gray-900 border-gray-800"> 133 <DropdownMenuItem className="text-yellow-400 focus:text-yellow-400 focus:bg-yellow-900/20"> 134 <Settings className="mr-2 h-4 w-4" /> 135 Change Role 136 </DropdownMenuItem> 137 <DropdownMenuItem className="text-red-400 focus:text-red-400 focus:bg-red-900/20"> 138 <LogOut className="mr-2 h-4 w-4" /> 139 Remove from Team 140 </DropdownMenuItem> 141 </DropdownMenuContent> 142 </DropdownMenu> 143 )} 144 </div> 145 ))} 146 </div> 147 </ScrollArea> 148 </SettingsLayout> 149 ) 150 }