forked from administration/panel
feat: user selector component
parent
7d7017168b
commit
3f5ec1f2ef
|
@ -1,6 +1,6 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { Server } from "revolt-api";
|
import { Server, User } from "revolt-api";
|
||||||
import { Button, buttonVariants } from "../../ui/button";
|
import { Button, buttonVariants } from "../../ui/button";
|
||||||
import {
|
import {
|
||||||
Command,
|
Command,
|
||||||
|
@ -22,12 +22,13 @@ import { DropdownMenuTrigger } from "@radix-ui/react-dropdown-menu";
|
||||||
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from "@/components/ui/alert-dialog";
|
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from "@/components/ui/alert-dialog";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Checkbox } from "@/components/ui/checkbox";
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
|
import UserSelector from "@/components/ui/user-selector";
|
||||||
|
|
||||||
export function ServerActions({ server }: { server: Server }) {
|
export function ServerActions({ server }: { server: Server }) {
|
||||||
const [selectBadges, setSelectBadges] = useState(false);
|
const [selectBadges, setSelectBadges] = useState(false);
|
||||||
const [serverDraft, setDraft] = useState(server);
|
const [serverDraft, setDraft] = useState(server);
|
||||||
const [newOwner, setNewOwner] = useState("");
|
const [newOwner, setNewOwner] = useState<User | null>(null);
|
||||||
const [newMember, setNewMember] = useState("");
|
const [newMember, setNewMember] = useState<User | null>(null);
|
||||||
const [newMemberEvent, setNewMemberEvent] = useState(true);
|
const [newMemberEvent, setNewMemberEvent] = useState(true);
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
|
|
||||||
|
@ -167,21 +168,19 @@ export function ServerActions({ server }: { server: Server }) {
|
||||||
</AlertDialogTitle>
|
</AlertDialogTitle>
|
||||||
<AlertDialogDescription className="flex flex-col gap-2">
|
<AlertDialogDescription className="flex flex-col gap-2">
|
||||||
Enter the ID of the new server owner.
|
Enter the ID of the new server owner.
|
||||||
<Input
|
<UserSelector
|
||||||
placeholder="01EXAF3KX65608AJ4NG27YG1HM"
|
onChange={(user) => setNewOwner(user)}
|
||||||
value={newOwner}
|
|
||||||
onChange={(e) => setNewOwner(e.currentTarget.value)}
|
|
||||||
/>
|
/>
|
||||||
</AlertDialogDescription>
|
</AlertDialogDescription>
|
||||||
</AlertDialogHeader>
|
</AlertDialogHeader>
|
||||||
<AlertDialogFooter>
|
<AlertDialogFooter>
|
||||||
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
||||||
<AlertDialogAction
|
<AlertDialogAction
|
||||||
disabled={newOwner.length != 26}
|
disabled={!newOwner}
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
try {
|
try {
|
||||||
await updateServerOwner(server._id, newOwner);
|
await updateServerOwner(server._id, newOwner!._id);
|
||||||
setNewOwner("");
|
setNewOwner(null);
|
||||||
toast({ title: "Server owner changed" });
|
toast({ title: "Server owner changed" });
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
toast({
|
toast({
|
||||||
|
@ -211,22 +210,18 @@ export function ServerActions({ server }: { server: Server }) {
|
||||||
</AlertDialogTitle>
|
</AlertDialogTitle>
|
||||||
<AlertDialogDescription className="flex flex-col gap-2">
|
<AlertDialogDescription className="flex flex-col gap-2">
|
||||||
Enter the ID of the user you want to add.
|
Enter the ID of the user you want to add.
|
||||||
<Input
|
<UserSelector onChange={(user) => setNewMember(user)} />
|
||||||
placeholder="01EXAF3KX65608AJ4NG27YG1HM"
|
|
||||||
value={newMember}
|
|
||||||
onChange={(e) => setNewMember(e.currentTarget.value)}
|
|
||||||
/>
|
|
||||||
<Checkbox checked={newMemberEvent} onChange={(state) => setNewMemberEvent(state === true)}>Publish join event</Checkbox>
|
<Checkbox checked={newMemberEvent} onChange={(state) => setNewMemberEvent(state === true)}>Publish join event</Checkbox>
|
||||||
</AlertDialogDescription>
|
</AlertDialogDescription>
|
||||||
</AlertDialogHeader>
|
</AlertDialogHeader>
|
||||||
<AlertDialogFooter>
|
<AlertDialogFooter>
|
||||||
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
||||||
<AlertDialogAction
|
<AlertDialogAction
|
||||||
disabled={newMember.length != 26}
|
disabled={!newMember}
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
try {
|
try {
|
||||||
await addServerMember(server._id, newMember, newMemberEvent);
|
await addServerMember(server._id, newMember!._id, newMemberEvent);
|
||||||
setNewMember("");
|
setNewMember(null);
|
||||||
toast({ title: "User added to server" });
|
toast({ title: "User added to server" });
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
toast({
|
toast({
|
||||||
|
|
|
@ -0,0 +1,88 @@
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { Input } from "./input";
|
||||||
|
import { Card, CardDescription, CardHeader, CardTitle } from "./card";
|
||||||
|
import { Avatar, AvatarFallback, AvatarImage } from "./avatar";
|
||||||
|
import { User } from "revolt-api";
|
||||||
|
import { AUTUMN_URL } from "@/lib/constants";
|
||||||
|
import { fetchUserById } from "@/lib/db";
|
||||||
|
|
||||||
|
export default function UserSelector({ onChange }: {
|
||||||
|
onChange?: (user: User | null) => any,
|
||||||
|
}) {
|
||||||
|
const [input, setInput] = useState("");
|
||||||
|
const [user, setUser] = useState<User | null>(null);
|
||||||
|
const [searching, setSearching] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (input.length != 26) {
|
||||||
|
onChange?.(null);
|
||||||
|
if (user) setUser(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!searching) return;
|
||||||
|
if (input != user?._id) {
|
||||||
|
setSearching(true);
|
||||||
|
fetchUserById(input)
|
||||||
|
.then((user) => {
|
||||||
|
setUser(user);
|
||||||
|
onChange?.(user);
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
setUser(null);
|
||||||
|
onChange?.(null);
|
||||||
|
})
|
||||||
|
.finally(() => setSearching(false));
|
||||||
|
}
|
||||||
|
else setUser(null);
|
||||||
|
}, [input, user, searching, onChange]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Input
|
||||||
|
className="rounded-b-none"
|
||||||
|
style={{ boxShadow: "none" }} // doing this with tailwind just... doesnt work
|
||||||
|
placeholder="Enter an ID..."
|
||||||
|
value={input}
|
||||||
|
onChange={(e) => {
|
||||||
|
setInput(e.currentTarget.value);
|
||||||
|
setSearching(true);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Card className="border-t-0 rounded-t-none">
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle className={`flex items-center gap-1 ${user ? "" : "text-gray-400"}`}>
|
||||||
|
<Avatar>
|
||||||
|
{user && <AvatarImage src={`${AUTUMN_URL}/avatars/${user.avatar?._id}`} />}
|
||||||
|
<AvatarFallback className="overflow-hidden overflow-ellipsis whitespace-nowrap">
|
||||||
|
{user
|
||||||
|
? (user.display_name ?? user.username)
|
||||||
|
.split(" ")
|
||||||
|
.slice(0, 2)
|
||||||
|
.map((x) => String.fromCodePoint(x.codePointAt(0) ?? 32) ?? "")
|
||||||
|
.join("")
|
||||||
|
: "?"}
|
||||||
|
</AvatarFallback>
|
||||||
|
</Avatar>
|
||||||
|
{user
|
||||||
|
? <>{user.username}#{user.discriminator} {user.display_name}</>
|
||||||
|
: "User#0000"
|
||||||
|
}
|
||||||
|
</CardTitle>
|
||||||
|
<CardDescription>
|
||||||
|
{
|
||||||
|
!input
|
||||||
|
? "Enter an ID..."
|
||||||
|
: input.length != 26
|
||||||
|
? "Invalid ID"
|
||||||
|
: searching
|
||||||
|
? "Searching..."
|
||||||
|
: user
|
||||||
|
? "User exists!"
|
||||||
|
: "Unknown user"
|
||||||
|
}
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
Loading…
Reference in New Issue