Skip to content

Commit

Permalink
Merge branch 'v3-main' into add-guild-page
Browse files Browse the repository at this point in the history
  • Loading branch information
BrickheadJohnny committed Nov 26, 2024
2 parents e6df6f7 + b5ffe17 commit dd69324
Show file tree
Hide file tree
Showing 62 changed files with 1,683 additions and 147 deletions.
8 changes: 3 additions & 5 deletions .storybook/preview.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Preview } from "@storybook/react";
import "../src/styles/globals.css";

import { dystopian, inter } from "@/lib/fonts";
import { dystopian } from "@/lib/fonts";
import { withThemeByClassName } from "@storybook/addon-themes";
import { useEffect } from "react";

Expand All @@ -20,14 +20,12 @@ const preview: Preview = {
light: "",
dark: "dark",
},
defaultTheme: "light",
defaultTheme: "dark",
}),
(storyFn) => {
useEffect(() => {
if (typeof document === "undefined") return;
document
?.querySelector("body")
?.classList.add(inter.variable, dystopian.variable);
document?.querySelector("body")?.classList.add(dystopian.variable);
}, []);

return storyFn();
Expand Down
10 changes: 5 additions & 5 deletions next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,11 +125,11 @@ const nextConfig = {
},
async redirects() {
return [
// {
// source: "/",
// destination: "/explorer",
// permanent: false,
// },
{
source: "/",
destination: "/explorer",
permanent: false,
},
{
source: "/buildonbase/:path*",
destination: "/base/:path*",
Expand Down
17 changes: 16 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"start": "next start",
"type-check": "tsc --pretty --noEmit --incremental false",
"write-check": "npx @biomejs/biome check --write --unsafe .",
"storybook": "storybook dev -p 6006",
"storybook": "storybook dev -p 6006 --no-open",
"build-storybook": "storybook build"
},
"dependencies": {
Expand All @@ -21,13 +21,15 @@
"@radix-ui/react-dialog": "^1.1.2",
"@radix-ui/react-focus-scope": "^1.1.0",
"@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-popover": "^1.1.2",
"@radix-ui/react-radio-group": "^1.2.1",
"@radix-ui/react-scroll-area": "^1.2.1",
"@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-switch": "^1.1.1",
"@radix-ui/react-toggle": "^1.1.0",
"@radix-ui/react-toggle-group": "^1.1.0",
"@t3-oss/env-nextjs": "^0.11.1",
"@tailwindcss/typography": "^0.5.15",
"@tanstack/react-query": "^5.60.2",
"@tanstack/react-query-devtools": "^5.61.0",
"autoprefixer": "^10.4.20",
Expand All @@ -36,13 +38,25 @@
"foxact": "^0.2.41",
"jotai": "^2.10.2",
"jwt-decode": "^4.0.0",
"mini-svg-data-uri": "^1.4.4",
"next": "15.0.3",
"next-themes": "^0.4.3",
"pinata-web3": "^0.5.2",
"react": "19.0.0-rc-66855b96-20241106",
"react-canvas-confetti": "^2.0.7",
"react-dom": "19.0.0-rc-66855b96-20241106",
"react-hook-form": "^7.53.2",
"react-markdown": "^9.0.1",
"rehype-external-links": "^3.0.0",
"rehype-slug": "^6.0.0",
"remark-gfm": "^4.0.0",
"remark-textr": "^6.1.0",
"server-only": "^0.0.1",
"slugify": "^1.6.6",
"sonner": "^1.7.0",
"tailwind-merge": "^2.5.4",
"tailwindcss-animate": "^1.0.7",
"typographic-base": "^1.0.4",
"vaul": "^1.1.1",
"viem": "^2.21.45",
"wagmi": "^2.12.32",
Expand All @@ -60,6 +74,7 @@
"@storybook/nextjs": "^8.4.4",
"@storybook/react": "^8.4.4",
"@storybook/test": "^8.4.4",
"@svgr/webpack": "^8.1.0",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
Expand Down
Binary file added public/favicon.ico
Binary file not shown.
Binary file added public/guild-xyz-brand-kit.zip
Binary file not shown.
122 changes: 122 additions & 0 deletions public/prose/privacyPolicy.md

Large diffs are not rendered by default.

108 changes: 108 additions & 0 deletions public/prose/termsOfUse.md

Large diffs are not rendered by default.

19 changes: 19 additions & 0 deletions src/actions/getPinataKey.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
"use server";

import { pinata } from "@/config/pinata.server";

export const getPinataKey = async () => {
const uuid = crypto.randomUUID();
const keyData = await pinata.keys.create({
keyName: uuid.toString(),
permissions: {
endpoints: {
pinning: {
pinFileToIPFS: true,
},
},
},
maxUses: 1,
});
return keyData;
};
13 changes: 5 additions & 8 deletions src/app/(dashboard)/[guildId]/[roleGroupId]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import { Button } from "@/components/ui/Button";
import { Card } from "@/components/ui/Card";
import { env } from "@/lib/env";
import type {
DynamicRoute,
Guild,
PaginatedResponse,
Role,
RoleGroup,
} from "@/lib/types";
import type { Guild } from "@/lib/schemas/guild";
import type { Role } from "@/lib/schemas/role";
import type { RoleGroup } from "@/lib/schemas/roleGroup";
import type { DynamicRoute, PaginatedResponse } from "@/lib/types";
import { Lock } from "@phosphor-icons/react/dist/ssr";
import { ScrollArea } from "@radix-ui/react-scroll-area";
import { Fragment } from "react";
Expand Down Expand Up @@ -44,7 +41,7 @@ const RoleGroupPage = async ({
<div className="flex items-center gap-3">
<img
className="size-14 rounded-full border"
src={role.imageUrl}
src={role.imageUrl ?? ""} // TODO: fallback image
alt="role avatar"
/>
<h3 className="font-bold text-xl tracking-tight">{role.name}</h3>
Expand Down
9 changes: 3 additions & 6 deletions src/app/(dashboard)/[guildId]/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,9 @@ import { DashboardContainer } from "@/components/DashboardContainer";
import { Button } from "@/components/ui/Button";
import { ScrollArea, ScrollBar } from "@/components/ui/ScrollArea";
import { env } from "@/lib/env";
import type {
DynamicRoute,
Guild,
PaginatedResponse,
RoleGroup,
} from "@/lib/types";
import type { Guild } from "@/lib/schemas/guild";
import type { RoleGroup } from "@/lib/schemas/roleGroup";
import type { DynamicRoute, PaginatedResponse } from "@/lib/types";
import type { PropsWithChildren } from "react";
import { RoleGroupNavLink } from "./_components/RoleGroupNavLink";

Expand Down
9 changes: 3 additions & 6 deletions src/app/(dashboard)/[guildId]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import { env } from "@/lib/env";
import type {
DynamicRoute,
Guild,
PaginatedResponse,
RoleGroup,
} from "@/lib/types";
import type { Guild } from "@/lib/schemas/guild";
import type { RoleGroup } from "@/lib/schemas/roleGroup";
import type { DynamicRoute, PaginatedResponse } from "@/lib/types";
import { redirect } from "next/navigation";

const DefaultGuildPage = async ({
Expand Down
27 changes: 27 additions & 0 deletions src/app/(dashboard)/[guild]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import type { Metadata } from "next";

type Props = {
params: Promise<{ guild: string }>;
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
};

export async function generateMetadata({ params }: Props): Promise<Metadata> {
const urlName = (await params).guild;

return {
title: urlName,
};
}

const GuildPage = async ({ params }: Props) => {
const urlName = (await params).guild;

return (
<div className="grid gap-4">
<p>Guild page</p>
<p>{`URL name: ${urlName}`}</p>
</div>
);
};

export default GuildPage;
80 changes: 80 additions & 0 deletions src/app/(dashboard)/create-guild/components/CreateGuildButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
"use client";

import { useConfetti } from "@/components/ConfettiProvider";
import { Button } from "@/components/ui/Button";
import { GUILD_AUTH_COOKIE_NAME } from "@/config/constants";
import { env } from "@/lib/env";
import { fetcher } from "@/lib/fetcher";
import { getCookie } from "@/lib/getCookie";
import type { CreateGuildForm, Guild } from "@/lib/schemas/guild";
import { CheckCircle, XCircle } from "@phosphor-icons/react/dist/ssr";
import { useMutation } from "@tanstack/react-query";
import { useRouter } from "next/navigation";
import { useFormContext } from "react-hook-form";
import slugify from "slugify";
import { toast } from "sonner";

const CreateGuildButton = () => {
const { handleSubmit } = useFormContext<CreateGuildForm>();

const confetti = useConfetti();

const router = useRouter();

const { mutate: onSubmit, isPending } = useMutation({
mutationFn: async (data: CreateGuildForm) => {
const token = getCookie(GUILD_AUTH_COOKIE_NAME);

if (!token) throw new Error("Unauthorized"); // TODO: custom errors?

const guild = {
...data,
contact: undefined,
// TODO: I think we should do it on the backend
urlName: slugify(data.name, {
replacement: "-",
lower: true,
strict: true,
}),
};

return fetcher<Guild>(`${env.NEXT_PUBLIC_API}/guild`, {
method: "POST",
headers: {
"X-Auth-Token": token,
"Content-Type": "application/json",
},
body: JSON.stringify(guild),
});
},
onError: (error) => {
// TODO: parse the error and display it in a user-friendly way
toast("An error occurred", {
icon: <XCircle weight="fill" className="text-icon-error" />,
});
console.error(error);
},
onSuccess: (res) => {
confetti.current();
toast("Guild successfully created", {
description: "You're being redirected to its page",
icon: <CheckCircle weight="fill" className="text-icon-success" />,
});
router.push(`/${res.urlName}`);
},
});

return (
<Button
colorScheme="success"
size="xl"
isLoading={isPending}
loadingText="Creating guild"
onClick={handleSubmit((data) => onSubmit(data))}
>
Create guild
</Button>
);
};

export { CreateGuildButton };
63 changes: 63 additions & 0 deletions src/app/(dashboard)/create-guild/components/CreateGuildForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
"use client";

import { useFormContext } from "react-hook-form";

import { ImageUploader } from "@/components/ImageUploader";
import {
FormControl,
FormErrorMessage,
FormField,
FormItem,
FormLabel,
} from "@/components/ui/Form";
import { Input } from "@/components/ui/Input";
import type { CreateGuildForm as CreateGuildFormType } from "@/lib/schemas/guild";

export const CreateGuildForm = () => {
const { control, setValue } = useFormContext<CreateGuildFormType>();

return (
<>
<div className="mx-auto size-32 rounded-full bg-input-background">
<ImageUploader
onSuccess={(imageUrl) =>
setValue("imageUrl", imageUrl, {
shouldDirty: true,
})
}
className="size-32"
/>
</div>

<FormField
control={control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel aria-required>Guild name</FormLabel>
<FormControl>
<Input size="lg" {...field} />
</FormControl>

<FormErrorMessage />
</FormItem>
)}
/>

<FormField
control={control}
name="contact"
render={({ field }) => (
<FormItem>
<FormLabel aria-required>E-mail address</FormLabel>
<FormControl>
<Input size="lg" {...field} />
</FormControl>

<FormErrorMessage />
</FormItem>
)}
/>
</>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"use client";

import { type CreateGuildForm, CreateGuildSchema } from "@/lib/schemas/guild";
import { zodResolver } from "@hookform/resolvers/zod";
import type { PropsWithChildren } from "react";
import { FormProvider, useForm } from "react-hook-form";

const defaultValues = {
name: "",
imageUrl: "",
urlName: "test",
contact: "",
} satisfies CreateGuildForm;

const CreateGuildFormProvider = ({ children }: PropsWithChildren) => {
const methods = useForm<CreateGuildForm>({
mode: "onTouched",
resolver: zodResolver(CreateGuildSchema),
defaultValues,
});

return <FormProvider {...methods}>{children}</FormProvider>;
};

export { CreateGuildFormProvider };
Loading

0 comments on commit dd69324

Please sign in to comment.