Skip to content

Commit

Permalink
Dynamic demo data
Browse files Browse the repository at this point in the history
  • Loading branch information
eduardozgz committed Oct 16, 2024
1 parent 87c67e4 commit 8341383
Show file tree
Hide file tree
Showing 29 changed files with 1,097 additions and 303 deletions.
57 changes: 56 additions & 1 deletion apps/website/src/@types/resources.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ interface Resources {
"admin": "Admin",
"manageUsers": "Manage users",
"manageServers": "Manage servers",
"manageHomePage": "Manage homepage",
"sentry": "Sentry",
"copyright": "© {{year}} Member Counter. All rights reserved. Created by <eduardozgzLink />.",
"madePossibleThanksTo": "Made possible thanks to Alex, <vampireChickenLink />, <livingfloreLink />, Frosty, and <donorsLink>many more</donorsLink>."
Expand Down Expand Up @@ -478,7 +479,8 @@ interface Resources {
"seeUsers": "See users",
"manageUsers": "Manage users",
"seeGuilds": "See servers",
"manageGuilds": "Manage servers"
"manageGuilds": "Manage servers",
"manageHomePage": "Manage landing page"
},
"badges": {
"title": "Badges",
Expand All @@ -500,6 +502,59 @@ interface Resources {
"loadUser": "Load user",
"recentUsers": "Recent users",
"userNotRegistered": "This user isn't registered."
},
"homePage": {
"demoServers": {
"title": "Demo servers",
"createInputPlaceholder": "New demo server name",
"createBtn": "Create",
"list": {
"display": {
"priority": "Priority: {{priority}}"
}
},
"manage": {
"title": "Edit demo server",
"name": "Server name",
"description": "Description",
"priority": "Priority",
"icon": "Icon URL",
"channels": {
"title": "Channels",
"add": "Add channel",
"channel": {
"name": "Name",
"type": "Type",
"types": {
"announcementChannel": "Announcement channel",
"voiceChannel": "Voice channel",
"categoryChannel": "Category channel",
"textChannel": "Text channel"
},
"topic": "Topic",
"showAsSkeleton": "Show as skeleton",
"remove": "Remove"
}
},
"links": {
"title": "Links",
"add": "Add link",
"link": {
"label": "Label",
"url": "URL",
"remove": "Remove"
}
},
"saved": "Saved",
"save": "Save"
},
"delete": {
"button": "Delete demo server",
"dialogTitle": "Are you absolutely sure?",
"dialogDescription": "This action cannot be undone.",
"closeButton": "Close"
}
}
}
}
},
Expand Down
38 changes: 38 additions & 0 deletions apps/website/src/app/admin/homepage/demo-servers/CreateInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { useState } from "react";
import { useRouter } from "next/navigation";
import { useTranslation } from "react-i18next";

import { Button } from "@mc/ui/button";
import { Input } from "@mc/ui/input";

import { Routes } from "~/other/routes";
import { api } from "~/trpc/react";

export function CreateInput() {
const { t } = useTranslation();
const [name, setName] = useState("");
const createDemoServer = api.demoServers.create.useMutation();
const router = useRouter();

const create = async () => {
if (!name) return;
const demoServer = await createDemoServer.mutateAsync({ name: name });
router.push(Routes.ManageHomeDemoServer(demoServer.id));
};

return (
<div className="flex flex-row gap-2">
<Input
value={name}
onChange={(e) => setName(e.target.value)}
onKeyDown={(e) => e.key === "Enter" && create()}
placeholder={t(
"pages.admin.homePage.demoServers.createInputPlaceholder",
)}
/>
<Button onClick={create} disabled={!name}>
{t("pages.admin.homePage.demoServers.createBtn")}
</Button>
</div>
);
}
26 changes: 26 additions & 0 deletions apps/website/src/app/admin/homepage/demo-servers/DemoServers.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { useTranslation } from "react-i18next";

import { Button } from "@mc/ui/button";
import { Card, CardContent, CardHeader } from "@mc/ui/card";
import { Input } from "@mc/ui/input";
import { TypographyH4 } from "@mc/ui/TypographyH4";

import { CreateInput } from "./CreateInput";
import { ListDemoServers } from "./ListDemoServers";

export function DemoServers() {
const { t } = useTranslation();
return (
<Card>
<CardHeader>
<TypographyH4 className="mt-0">
{t("pages.admin.homePage.demoServers.title")}
</TypographyH4>
</CardHeader>
<CardContent className="flex flex-col gap-2">
<CreateInput />
<ListDemoServers />
</CardContent>
</Card>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
"use client";

import { useTranslation } from "react-i18next";

/* eslint-disable @next/next/no-img-element */
export const DisplayDemoServer = ({
name,
icon,
priority,
}: {
name: string;
priority: number;
icon?: string | null;
}) => {
const { t } = useTranslation();

return (
<div className="flex w-full flex-row items-center gap-2">
{icon && (
<img src={icon} alt={``} aria-hidden className="h-8 w-8 rounded-md" />
)}
<div>
<div>{name}</div>
<div className="text-sm text-muted-foreground">
{t("pages.admin.homePage.demoServers.list.display.priority", {
priority: priority.toString(),
})}
</div>
</div>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
"use client";

import { useRouter } from "next/navigation";

import { Button } from "@mc/ui/button";

import { Routes } from "~/other/routes";
import { api } from "~/trpc/react";
import { DisplayDemoServer } from "./DisplayDemoServer";

export const ListDemoServers = () => {
const demoServers = api.demoServers.geAll.useQuery();
const router = useRouter();

return (
<div className="flex flex-col gap-2">
{demoServers.data?.map((demoServer) => (
<Button
onClick={() =>
router.push(Routes.ManageHomeDemoServer(demoServer.id))
}
className="py-8 text-start"
variant="ghost"
key={demoServer.id}
>
<DisplayDemoServer {...demoServer} />
</Button>
))}
</div>
);
};
149 changes: 149 additions & 0 deletions apps/website/src/app/admin/homepage/demo-servers/[id]/ChannelCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import type { DemoServerData } from "@mc/common/DemoServers";
import { useId } from "react";
import { ChannelType } from "discord-api-types/v10";
import { TrashIcon } from "lucide-react";
import { useTranslation } from "react-i18next";

import { Button } from "@mc/ui/button";
import { Card } from "@mc/ui/card";
import { Checkbox } from "@mc/ui/checkbox";
import { Input } from "@mc/ui/input";
import { Label } from "@mc/ui/label";
import {
Select,
SelectContent,
SelectGroup,
SelectTrigger,
SelectValue,
} from "@mc/ui/select";
import { SelectItemWithIcon } from "@mc/ui/selectItemWithIcon";

import { ChannelIconMap } from "~/app/dashboard/servers/[guildId]/ChannelMaps";

interface ChannelCardProps {
channel: DemoServerData["channels"][number];
index: number;
updateChannel: (
index: number,
channel: DemoServerData["channels"][number],
) => void;
removeChannel: (index: number) => void;
}

export function ChannelCard({
channel,
index,
updateChannel,
removeChannel,
}: ChannelCardProps) {
const { t } = useTranslation();
const skeletonCheckboxId = useId();
return (
<Card className="flex flex-col gap-4 bg-secondary p-4 [&>*]:flex [&>*]:flex-col [&>*]:gap-2">
<div>
<Label>
{t("pages.admin.homePage.demoServers.manage.channels.channel.type")}
</Label>
<Select
value={channel.type.toString()}
onValueChange={(value) =>
updateChannel(index, { ...channel, type: Number(value) })
}
>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectItemWithIcon
value={ChannelType.GuildAnnouncement.toString()}
label={t(
"pages.admin.homePage.demoServers.manage.channels.channel.types.announcementChannel",
)}
icon={ChannelIconMap[ChannelType.GuildAnnouncement]}
/>
<SelectItemWithIcon
value={ChannelType.GuildText.toString()}
label={t(
"pages.admin.homePage.demoServers.manage.channels.channel.types.textChannel",
)}
icon={ChannelIconMap[ChannelType.GuildText]}
/>

<SelectItemWithIcon
value={ChannelType.GuildVoice.toString()}
label={t(
"pages.admin.homePage.demoServers.manage.channels.channel.types.voiceChannel",
)}
icon={ChannelIconMap[ChannelType.GuildVoice]}
/>

<SelectItemWithIcon
value={ChannelType.GuildCategory.toString()}
label={t(
"pages.admin.homePage.demoServers.manage.channels.channel.types.categoryChannel",
)}
icon={ChannelIconMap[ChannelType.GuildCategory]}
/>
</SelectGroup>
</SelectContent>
</Select>
</div>
<div>
<Label>
{t("pages.admin.homePage.demoServers.manage.channels.channel.name")}
</Label>
<Input
value={channel.name}
onChange={(e) =>
updateChannel(index, { ...channel, name: e.target.value })
}
/>
</div>
<div>
<Label>
{t("pages.admin.homePage.demoServers.manage.channels.channel.topic")}
</Label>
{[ChannelType.GuildText, ChannelType.GuildAnnouncement].includes(
channel.type,
) && (
<Input
value={channel.topic ?? ""}
onChange={(e) =>
updateChannel(index, { ...channel, topic: e.target.value })
}
/>
)}
</div>
<div>
<div className="flex items-center gap-2">
<Checkbox
id={skeletonCheckboxId}
checked={channel.showAsSkeleton}
onCheckedChange={(state) => {
updateChannel(index, {
...channel,
showAsSkeleton: Boolean(state),
});
}}
/>
<Label htmlFor={skeletonCheckboxId}>
{t(
"pages.admin.homePage.demoServers.manage.channels.channel.showAsSkeleton",
)}
</Label>
</div>
</div>
<div>
<Button
icon={TrashIcon}
variant="destructive"
type="button"
onClick={() => removeChannel(index)}
>
{t("pages.admin.homePage.demoServers.manage.channels.channel.remove")}
</Button>
</div>
</Card>
);
}
Loading

0 comments on commit 8341383

Please sign in to comment.