The weeb for the next gen discord boat - Wamellow wamellow.com
bot discord
3
fork

Configure Feed

Select the types of activity you want to include in your feed.

rename permission component file

Luna 235eec7c c827030c

+297 -297
+208 -208
app/dashboard/[guildId]/leaderboards/page.tsx
··· 1 - "use client"; 2 - import Image from "next/image"; 3 - import { useParams } from "next/navigation"; 4 - import { useCookies } from "next-client-cookies"; 5 - import { HiChartBar, HiViewGridAdd } from "react-icons/hi"; 6 - import { useQuery } from "react-query"; 7 - 8 - import { Guild, guildStore } from "@/common/guilds"; 9 - import ImageUrlInput from "@/components/inputs/image-url-input"; 10 - import MultiSelectMenu from "@/components/inputs/multi-select-menu"; 11 - import TextInput from "@/components/inputs/text-input"; 12 - import { ScreenMessage } from "@/components/screen-message"; 13 - import { Section, SubSection } from "@/components/section"; 14 - import { cacheOptions, getData } from "@/lib/api"; 15 - import SadWumpusPic from "@/public/sad-wumpus.gif"; 16 - import { ApiV1GuildsModulesLeaderboardGetResponse } from "@/typings"; 17 - 18 - import OverviewLinkComponent from "../../../../components/OverviewLinkComponent"; 19 - import Permissions from "./permissions"; 20 - import ResetLeaderboard from "./reset.component"; 21 - import UpdatingLeaderboardCard from "./updating.component"; 22 - import DiscordWidget from "./widget.component"; 23 - 24 - export default function Home() { 25 - const cookies = useCookies(); 26 - 27 - const guild = guildStore((g) => g); 28 - const params = useParams(); 29 - 30 - const url = `/guilds/${params.guildId}/modules/leaderboard` as const; 31 - 32 - const { data, isLoading, error } = useQuery( 33 - url, 34 - () => getData<ApiV1GuildsModulesLeaderboardGetResponse>(url), 35 - { 36 - enabled: !!params.guildId, 37 - ...cacheOptions, 38 - refetchOnMount: true 39 - } 40 - ); 41 - 42 - if (error || (data && "message" in data)) { 43 - return ( 44 - <ScreenMessage 45 - top="0rem" 46 - title="Something went wrong on this page.." 47 - description={ 48 - (data && "message" in data ? data.message : `${error}`) 49 - || "An unknown error occurred."} 50 - href={`/dashboard/${guild?.id}`} 51 - button="Go back to overview" 52 - icon={<HiViewGridAdd />} 53 - > 54 - <Image src={SadWumpusPic} alt="" height={141} width={124} /> 55 - </ScreenMessage> 56 - ); 57 - } 58 - 59 - if (isLoading || !data) return <></>; 60 - 61 - return ( 62 - <div> 63 - 64 - <div className="flex flex-col-reverse md:flex-row gap-6"> 65 - <div> 66 - <OverviewLinkComponent 67 - title="View Leaderboard" 68 - message="Easily access and view the top chatters, voice timers, and inviters from this server in the web." 69 - url={`/leaderboard/${params.guildId}`} 70 - icon={<HiChartBar />} 71 - /> 72 - 73 - {cookies.get("devTools") && 74 - <div className={"flex gap-4 border-2 border-violet-400 p-4 mb-4 rounded-lg"}> 75 - 76 - <div className="lg:w-1/2 flex gap-2 w-full"> 77 - 78 - <div className="w-1/2"> 79 - <TextInput 80 - name="Text" 81 - url={url} 82 - dataName="textColor" 83 - description="Color used for text." 84 - type="color" 85 - defaultState={data.textColor ?? 0xe5e5e5} 86 - resetState={0xe5e5e5} 87 - /> 88 - </div> 89 - 90 - <div className="w-1/2"> 91 - <TextInput 92 - name="Accent" 93 - url={url} 94 - dataName="accentColor" 95 - description="Color used for secondary text." 96 - type="color" 97 - defaultState={data.accentColor ?? 0x8b5cf6} 98 - resetState={0x8b5cf6} 99 - /> 100 - </div> 101 - 102 - </div> 103 - 104 - <div className="w-1/2"> 105 - <TextInput 106 - name="Background" 107 - url={url} 108 - dataName="backgroundColor" 109 - description="Color used for the background." 110 - type="color" 111 - defaultState={data.backgroundColor ?? 0x0d0f11} 112 - resetState={0x0d0f11} 113 - /> 114 - </div> 115 - 116 - </div> 117 - } 118 - 119 - <ImageUrlInput 120 - name="Banner" 121 - url={url} 122 - ratio="aspect-[4/1]" 123 - dataName="banner" 124 - description="Enter a url which should be the banner of the leaderboard web page. The recomended image ration is 4:1 and recommended resolution 1024x256px." 125 - defaultState={data.bannerUrl || ""} 126 - /> 127 - </div> 128 - 129 - <Permissions 130 - className="w-full md:w-1/3 lg:w-1/4 shrink-0" 131 - guild={guild} 132 - /> 133 - 134 - </div> 135 - 136 - <Section 137 - title="Roles" 138 - > 139 - Select roles which should be assigned to the top members. (left to right - 1st to 3rd place) 140 - </Section> 141 - 142 - <div className="lg:flex gap-3 mt-5"> 143 - <div className="lg:w-1/2"> 144 - <MultiSelectMenu 145 - name="Top messager roles" 146 - url={url} 147 - dataName="roles.messages" 148 - items={guild?.roles?.sort((a, b) => b.position - a.position).map((r) => ({ name: `@${r.name}`, value: r.id, error: r.missingPermissions.join(", "), color: r.color }))} 149 - description="Select roles which should be assigned to the top message members." 150 - defaultState={data.roles?.messages || []} 151 - max={3} 152 - /> 153 - </div> 154 - <div className="lg:w-1/2"> 155 - <MultiSelectMenu 156 - name="Top voice roles" 157 - url={url} 158 - dataName="roles.voiceminutes" 159 - items={guild?.roles?.sort((a, b) => b.position - a.position).map((r) => ({ name: `@${r.name}`, value: r.id, error: r.missingPermissions.join(", "), color: r.color }))} 160 - description="Select roles which should be assigned to the top voice members." 161 - defaultState={data.roles?.voiceminutes || []} 162 - max={3} 163 - /> 164 - </div> 165 - </div> 166 - 167 - <div className="lg:w-1/2"> 168 - <MultiSelectMenu 169 - name="Blacklisted channels" 170 - url={url} 171 - dataName="blacklistChannelIds" 172 - items={guild?.channels?.sort((a, b) => a.name.localeCompare(b.name)).map((c) => ({ name: `#${c.name}`, value: c.id }))} 173 - description="Select channels which should not be able to be counted." 174 - defaultState={data.blacklistChannelIds || []} 175 - max={500} 176 - /> 177 - </div> 178 - 179 - <Section 180 - title="Updating" 181 - > 182 - These leaderboards will be sent in a channel and update roughly every 20 minutes. 183 - </Section> 184 - 185 - <div className="w-full grid gap-4 md:flex md:gap-0 md:items-center my-10"> 186 - <UpdatingLeaderboardCard guild={guild as Guild} lb={data.updating.find((lb) => lb.type === "messages")} type="messages" /> 187 - <UpdatingLeaderboardCard guild={guild as Guild} lb={data.updating.find((lb) => lb.type === "voiceminutes")} type="voiceminutes" /> 188 - <UpdatingLeaderboardCard guild={guild as Guild} lb={data.updating.find((lb) => lb.type === "invites")} type="invites" /> 189 - </div> 190 - 191 - 192 - <Section 193 - title="Privacy" 194 - > 195 - Manage the privacy of the leaderboard. 196 - </Section> 197 - 198 - <DiscordWidget guild={guild as Guild} /> 199 - 200 - <SubSection 201 - title="Removal" 202 - description="Reset the leaderboard to start fresh. This action cannot be undone" 203 - > 204 - <ResetLeaderboard guild={guild as Guild} /> 205 - </SubSection> 206 - 207 - </div > 208 - ); 1 + "use client"; 2 + import Image from "next/image"; 3 + import { useParams } from "next/navigation"; 4 + import { useCookies } from "next-client-cookies"; 5 + import { HiChartBar, HiViewGridAdd } from "react-icons/hi"; 6 + import { useQuery } from "react-query"; 7 + 8 + import { Guild, guildStore } from "@/common/guilds"; 9 + import ImageUrlInput from "@/components/inputs/image-url-input"; 10 + import MultiSelectMenu from "@/components/inputs/multi-select-menu"; 11 + import TextInput from "@/components/inputs/text-input"; 12 + import { ScreenMessage } from "@/components/screen-message"; 13 + import { Section, SubSection } from "@/components/section"; 14 + import { cacheOptions, getData } from "@/lib/api"; 15 + import SadWumpusPic from "@/public/sad-wumpus.gif"; 16 + import { ApiV1GuildsModulesLeaderboardGetResponse } from "@/typings"; 17 + 18 + import OverviewLinkComponent from "../../../../components/OverviewLinkComponent"; 19 + import Permissions from "./permissions.component"; 20 + import ResetLeaderboard from "./reset.component"; 21 + import UpdatingLeaderboardCard from "./updating.component"; 22 + import DiscordWidget from "./widget.component"; 23 + 24 + export default function Home() { 25 + const cookies = useCookies(); 26 + 27 + const guild = guildStore((g) => g); 28 + const params = useParams(); 29 + 30 + const url = `/guilds/${params.guildId}/modules/leaderboard` as const; 31 + 32 + const { data, isLoading, error } = useQuery( 33 + url, 34 + () => getData<ApiV1GuildsModulesLeaderboardGetResponse>(url), 35 + { 36 + enabled: !!params.guildId, 37 + ...cacheOptions, 38 + refetchOnMount: true 39 + } 40 + ); 41 + 42 + if (error || (data && "message" in data)) { 43 + return ( 44 + <ScreenMessage 45 + top="0rem" 46 + title="Something went wrong on this page.." 47 + description={ 48 + (data && "message" in data ? data.message : `${error}`) 49 + || "An unknown error occurred."} 50 + href={`/dashboard/${guild?.id}`} 51 + button="Go back to overview" 52 + icon={<HiViewGridAdd />} 53 + > 54 + <Image src={SadWumpusPic} alt="" height={141} width={124} /> 55 + </ScreenMessage> 56 + ); 57 + } 58 + 59 + if (isLoading || !data) return <></>; 60 + 61 + return ( 62 + <div> 63 + 64 + <div className="flex flex-col-reverse md:flex-row gap-6"> 65 + <div> 66 + <OverviewLinkComponent 67 + title="View Leaderboard" 68 + message="Easily access and view the top chatters, voice timers, and inviters from this server in the web." 69 + url={`/leaderboard/${params.guildId}`} 70 + icon={<HiChartBar />} 71 + /> 72 + 73 + {cookies.get("devTools") && 74 + <div className={"flex gap-4 border-2 border-violet-400 p-4 mb-4 rounded-lg"}> 75 + 76 + <div className="lg:w-1/2 flex gap-2 w-full"> 77 + 78 + <div className="w-1/2"> 79 + <TextInput 80 + name="Text" 81 + url={url} 82 + dataName="textColor" 83 + description="Color used for text." 84 + type="color" 85 + defaultState={data.textColor ?? 0xe5e5e5} 86 + resetState={0xe5e5e5} 87 + /> 88 + </div> 89 + 90 + <div className="w-1/2"> 91 + <TextInput 92 + name="Accent" 93 + url={url} 94 + dataName="accentColor" 95 + description="Color used for secondary text." 96 + type="color" 97 + defaultState={data.accentColor ?? 0x8b5cf6} 98 + resetState={0x8b5cf6} 99 + /> 100 + </div> 101 + 102 + </div> 103 + 104 + <div className="w-1/2"> 105 + <TextInput 106 + name="Background" 107 + url={url} 108 + dataName="backgroundColor" 109 + description="Color used for the background." 110 + type="color" 111 + defaultState={data.backgroundColor ?? 0x0d0f11} 112 + resetState={0x0d0f11} 113 + /> 114 + </div> 115 + 116 + </div> 117 + } 118 + 119 + <ImageUrlInput 120 + name="Banner" 121 + url={url} 122 + ratio="aspect-[4/1]" 123 + dataName="banner" 124 + description="Enter a url which should be the banner of the leaderboard web page. The recomended image ration is 4:1 and recommended resolution 1024x256px." 125 + defaultState={data.bannerUrl || ""} 126 + /> 127 + </div> 128 + 129 + <Permissions 130 + className="w-full md:w-1/3 lg:w-1/4 shrink-0" 131 + guild={guild} 132 + /> 133 + 134 + </div> 135 + 136 + <Section 137 + title="Roles" 138 + > 139 + Select roles which should be assigned to the top members. (left to right - 1st to 3rd place) 140 + </Section> 141 + 142 + <div className="lg:flex gap-3 mt-5"> 143 + <div className="lg:w-1/2"> 144 + <MultiSelectMenu 145 + name="Top messager roles" 146 + url={url} 147 + dataName="roles.messages" 148 + items={guild?.roles?.sort((a, b) => b.position - a.position).map((r) => ({ name: `@${r.name}`, value: r.id, error: r.missingPermissions.join(", "), color: r.color }))} 149 + description="Select roles which should be assigned to the top message members." 150 + defaultState={data.roles?.messages || []} 151 + max={3} 152 + /> 153 + </div> 154 + <div className="lg:w-1/2"> 155 + <MultiSelectMenu 156 + name="Top voice roles" 157 + url={url} 158 + dataName="roles.voiceminutes" 159 + items={guild?.roles?.sort((a, b) => b.position - a.position).map((r) => ({ name: `@${r.name}`, value: r.id, error: r.missingPermissions.join(", "), color: r.color }))} 160 + description="Select roles which should be assigned to the top voice members." 161 + defaultState={data.roles?.voiceminutes || []} 162 + max={3} 163 + /> 164 + </div> 165 + </div> 166 + 167 + <div className="lg:w-1/2"> 168 + <MultiSelectMenu 169 + name="Blacklisted channels" 170 + url={url} 171 + dataName="blacklistChannelIds" 172 + items={guild?.channels?.sort((a, b) => a.name.localeCompare(b.name)).map((c) => ({ name: `#${c.name}`, value: c.id }))} 173 + description="Select channels which should not be able to be counted." 174 + defaultState={data.blacklistChannelIds || []} 175 + max={500} 176 + /> 177 + </div> 178 + 179 + <Section 180 + title="Updating" 181 + > 182 + These leaderboards will be sent in a channel and update roughly every 20 minutes. 183 + </Section> 184 + 185 + <div className="w-full grid gap-4 md:flex md:gap-0 md:items-center my-10"> 186 + <UpdatingLeaderboardCard guild={guild as Guild} lb={data.updating.find((lb) => lb.type === "messages")} type="messages" /> 187 + <UpdatingLeaderboardCard guild={guild as Guild} lb={data.updating.find((lb) => lb.type === "voiceminutes")} type="voiceminutes" /> 188 + <UpdatingLeaderboardCard guild={guild as Guild} lb={data.updating.find((lb) => lb.type === "invites")} type="invites" /> 189 + </div> 190 + 191 + 192 + <Section 193 + title="Privacy" 194 + > 195 + Manage the privacy of the leaderboard. 196 + </Section> 197 + 198 + <DiscordWidget guild={guild as Guild} /> 199 + 200 + <SubSection 201 + title="Removal" 202 + description="Reset the leaderboard to start fresh. This action cannot be undone" 203 + > 204 + <ResetLeaderboard guild={guild as Guild} /> 205 + </SubSection> 206 + 207 + </div > 208 + ); 209 209 }
+89 -89
app/dashboard/[guildId]/leaderboards/permissions.tsx app/dashboard/[guildId]/leaderboards/permissions.component.tsx
··· 1 - import { Tooltip } from "@nextui-org/react"; 2 - import { HiExclamation } from "react-icons/hi"; 3 - 4 - import { Guild } from "@/common/guilds"; 5 - import DiscordChannel from "@/components/discord/channel"; 6 - import { ApiV1GuildsChannelsGetResponse } from "@/typings"; 7 - import cn from "@/utils/cn"; 8 - 9 - interface Props { 10 - className: string; 11 - guild: Guild | undefined; 12 - } 13 - 14 - const MAX_CHANNELS = 4; 15 - 16 - export default function Permissions({ 17 - className, 18 - guild 19 - }: Props) { 20 - 21 - const channels = guild?.channels 22 - ?.filter((c) => c.missingPermissions.includes("ViewChannel")) || []; 23 - 24 - if (!channels.length) return <></>; 25 - 26 - return ( 27 - <div 28 - className={cn("p-4 rounded-xl w-1/4 bg-wamellow h-fit relative", className)} 29 - > 30 - <h3 className="text-lg font-medium text-neutral-200"> 31 - {channels.length 32 - ? "Missing Access Permission" 33 - : "Permissions are all set! 🎉" 34 - } 35 - </h3> 36 - 37 - <Channels channels={channels} /> 38 - </div> 39 - ); 40 - } 41 - 42 - function Channels({ 43 - channels 44 - }: { 45 - channels: ApiV1GuildsChannelsGetResponse[]; 46 - }) { 47 - return ( 48 - <> 49 - <p className="text-sm opacity-75 mb-3"> 50 - Wammellow cannot track acivity in {channels.length} channels as it is missing permission. 51 - </p> 52 - 53 - <div className="flex flex-col gap-1"> 54 - 55 - {channels.map((c, i) => { 56 - if (i >= MAX_CHANNELS) return null; 57 - return ( 58 - <div 59 - className="flex justify-between items-center" 60 - key={"lbperms-" + c.id} 61 - > 62 - <DiscordChannel 63 - type="text" 64 - name={c.name} 65 - isTruncated 66 - /> 67 - <Tooltip 68 - content={"Missing View Channel"} 69 - placement="right" 70 - className="bg-[#030206]" 71 - closeDelay={0} 72 - > 73 - <span> 74 - <HiExclamation className="text-red-500" /> 75 - </span> 76 - </Tooltip> 77 - </div> 78 - ); 79 - })} 80 - </div> 81 - 82 - { 83 - channels.length > MAX_CHANNELS && 84 - <p className="text-sm opacity-75 relative bottom-1 mt-2"> 85 - +{channels.length - MAX_CHANNELS} more 86 - </p> 87 - } 88 - </> 89 - ); 1 + import { Tooltip } from "@nextui-org/react"; 2 + import { HiExclamation } from "react-icons/hi"; 3 + 4 + import { Guild } from "@/common/guilds"; 5 + import DiscordChannel from "@/components/discord/channel"; 6 + import { ApiV1GuildsChannelsGetResponse } from "@/typings"; 7 + import cn from "@/utils/cn"; 8 + 9 + interface Props { 10 + className: string; 11 + guild: Guild | undefined; 12 + } 13 + 14 + const MAX_CHANNELS = 4; 15 + 16 + export default function Permissions({ 17 + className, 18 + guild 19 + }: Props) { 20 + 21 + const channels = guild?.channels 22 + ?.filter((c) => c.missingPermissions.includes("ViewChannel")) || []; 23 + 24 + if (!channels.length) return <></>; 25 + 26 + return ( 27 + <div 28 + className={cn("p-4 rounded-xl w-1/4 bg-wamellow h-fit relative", className)} 29 + > 30 + <h3 className="text-lg font-medium text-neutral-200"> 31 + {channels.length 32 + ? "Missing Access Permission" 33 + : "Permissions are all set! 🎉" 34 + } 35 + </h3> 36 + 37 + <Channels channels={channels} /> 38 + </div> 39 + ); 40 + } 41 + 42 + function Channels({ 43 + channels 44 + }: { 45 + channels: ApiV1GuildsChannelsGetResponse[]; 46 + }) { 47 + return ( 48 + <> 49 + <p className="text-sm opacity-75 mb-3"> 50 + Wammellow cannot track acivity in {channels.length} channels as it is missing permission. 51 + </p> 52 + 53 + <div className="flex flex-col gap-1"> 54 + 55 + {channels.map((c, i) => { 56 + if (i >= MAX_CHANNELS) return null; 57 + return ( 58 + <div 59 + className="flex justify-between items-center" 60 + key={"lbperms-" + c.id} 61 + > 62 + <DiscordChannel 63 + type="text" 64 + name={c.name} 65 + isTruncated 66 + /> 67 + <Tooltip 68 + content={"Missing View Channel"} 69 + placement="right" 70 + className="bg-[#030206]" 71 + closeDelay={0} 72 + > 73 + <span> 74 + <HiExclamation className="text-red-500" /> 75 + </span> 76 + </Tooltip> 77 + </div> 78 + ); 79 + })} 80 + </div> 81 + 82 + { 83 + channels.length > MAX_CHANNELS && 84 + <p className="text-sm opacity-75 relative bottom-1 mt-2"> 85 + +{channels.length - MAX_CHANNELS} more 86 + </p> 87 + } 88 + </> 89 + ); 90 90 }