Skip to content

Commit

Permalink
feat: many things
Browse files Browse the repository at this point in the history
  • Loading branch information
jonasschultheiss committed Oct 29, 2023
1 parent aa5950c commit 47effea
Show file tree
Hide file tree
Showing 17 changed files with 491 additions and 34 deletions.
16 changes: 16 additions & 0 deletions components.json
@@ -0,0 +1,16 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "default",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "tailwind.config.ts",
"css": "src/styles/globals.css",
"baseColor": "slate",
"cssVariables": true
},
"aliases": {
"components": "src/app/_components",
"utils": "src/lib/utils"
}
}
14 changes: 13 additions & 1 deletion package.json
Expand Up @@ -12,23 +12,34 @@
},
"dependencies": {
"@clerk/nextjs": "^4.26.1",
"@clerk/themes": "^1.7.9",
"@heroicons/react": "^2.0.18",
"@planetscale/database": "^1.11.0",
"@t3-oss/env-nextjs": "^0.7.0",
"@tanstack/react-query": "^4.32.6",
"@trpc/client": "^10.37.1",
"@trpc/next": "^10.37.1",
"@trpc/react-query": "^10.37.1",
"@trpc/server": "^10.37.1",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"drizzle-orm": "^0.28.6",
"lucide-react": "^0.290.0",
"next": "^13.5.4",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-hook-form": "^7.47.0",
"superjson": "^1.13.1",
"tailwind-merge": "^1.14.0",
"tailwindcss-animate": "^1.0.7",
"zod": "^3.22.4"
},
"devDependencies": {
"@tailwindcss/aspect-ratio": "^0.4.2",
"@tailwindcss/forms": "^0.5.6",
"@tailwindcss/typography": "^0.5.10",
"@types/eslint": "^8.44.2",
"@types/node": "^18.16.0",
"@types/node": "^20.8.9",
"@types/react": "^18.2.20",
"@types/react-dom": "^18.2.7",
"@typescript-eslint/eslint-plugin": "^6.3.0",
Expand All @@ -42,6 +53,7 @@
"postcss": "^8.4.27",
"prettier": "^3.0.0",
"prettier-plugin-tailwindcss": "^0.5.1",
"server-only": "^0.0.1",
"tailwindcss": "^3.3.3",
"typescript": "^5.1.6"
},
Expand Down
22 changes: 22 additions & 0 deletions src/app/_components/background-wrapper.tsx
@@ -0,0 +1,22 @@
import Image from "next/image";
import type { ReactNode } from "react";

export type WrapperProps = {
children: ReactNode;
};

export const BackgroundWrapper: React.FC<WrapperProps> = ({
children,
}): JSX.Element => {
return (
<div className="max-w-xs p-4 pt-16">
<Image
src="/pattern.png"
alt="A background pattern"
fill
className="absolute left-0 right-0 top-0 -z-10"
/>
{children}
</div>
);
};
62 changes: 62 additions & 0 deletions src/app/_components/form.tsx
@@ -0,0 +1,62 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-return */
import type { DetailedHTMLProps, FormHTMLAttributes, ReactNode } from "react";
import type {
FieldValues,
SubmitHandler,
UseFormHandleSubmit,
UseFormReset,
} from "react-hook-form";

export type FeaturesProps = {
children: ReactNode;
};

interface FormProps<T extends FieldValues>
extends Omit<
DetailedHTMLProps<FormHTMLAttributes<HTMLFormElement>, HTMLFormElement>,
"onSubmit"
> {
submitText?: string;
resetButton: boolean;
handleSubmit: UseFormHandleSubmit<T>;
reset: UseFormReset<T>;
onSubmit: SubmitHandler<T>;
loading: boolean;
}

export function Form<T extends FieldValues>({
children,
submitText = "Submit",
resetButton,
handleSubmit,
reset,
onSubmit,
loading,
}: FormProps<T>): JSX.Element {
return (
<form onSubmit={handleSubmit(onSubmit)}>
<div className="space-y-12">{children}</div>

<div className="mt-6 flex items-center justify-end gap-x-6">
{resetButton && (
<button
onClick={() => reset}
type="reset"
className="text-sm font-semibold leading-6 text-white"
>
Reset
</button>
)}
<button
type="submit"
disabled={loading}
className="rounded-md bg-indigo-500 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-400 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-500"
>
{submitText}
</button>
</div>
</form>
);
}
15 changes: 15 additions & 0 deletions src/app/_components/ui/skeleton.tsx
@@ -0,0 +1,15 @@
import { cn } from "src/lib/utils"

function Skeleton({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) {
return (
<div
className={cn("animate-pulse rounded-md bg-muted", className)}
{...props}
/>
)
}

export { Skeleton }
23 changes: 17 additions & 6 deletions src/app/layout.tsx
@@ -1,12 +1,14 @@
import "~/styles/globals.css";
import "@styles/globals.css";

import { ClerkProvider } from "@clerk/nextjs";
import { Inter } from "next/font/google";
import { Inter as FontSans } from "next/font/google";
import { headers } from "next/headers";

import { TRPCReactProvider } from "~/trpc/react";
import { BackgroundWrapper } from "@components/background-wrapper";
import { cn } from "@lib/utils";
import { TRPCReactProvider } from "@trpc/react";

const inter = Inter({
export const fontSans = FontSans({
subsets: ["latin"],
variable: "--font-sans",
});
Expand All @@ -25,8 +27,17 @@ export default function RootLayout({
return (
<ClerkProvider>
<html lang="en">
<body className={`font-sans ${inter.variable}`}>
<TRPCReactProvider headers={headers()}>{children}</TRPCReactProvider>
<body
className={cn(
"bg-background min-h-screen font-sans antialiased",
fontSans.variable,
)}
>
<BackgroundWrapper>
<TRPCReactProvider headers={headers()}>
{children}
</TRPCReactProvider>
</BackgroundWrapper>
</body>
</html>
</ClerkProvider>
Expand Down
7 changes: 0 additions & 7 deletions src/app/page.tsx
Expand Up @@ -5,13 +5,6 @@ export default function Home() {

return (
<main>
<Image
src="/pattern.png"
alt="A background pattern"
fill
className="absolute left-0 right-0 top-0 -z-10"
/>

<Image
src="/initial.png"
alt="A background pattern"
Expand Down
16 changes: 16 additions & 0 deletions src/app/sign-up/layout.tsx
@@ -0,0 +1,16 @@
import { Suspense } from "react";
import Loading from "./loading";

export default function Layout({ children }: { children: React.ReactNode }) {
return (
<main>
<h1 className="mt-4 block text-2xl font-medium">
Fill in your bio to get started
</h1>
<p className="mt-4 block text-sm">
Data will get displayed to other users
</p>
<Suspense fallback={<Loading />}>{children}</Suspense>
</main>
);
}
13 changes: 13 additions & 0 deletions src/app/sign-up/loading.tsx
@@ -0,0 +1,13 @@
import { Skeleton } from "@components/ui/skeleton";

export default function Loading() {
return (
<div className="flex items-center space-x-4">
<Skeleton className="h-12 w-12 rounded-full" />
<div className="space-y-2">
<Skeleton className="h-4 w-[250px]" />
<Skeleton className="h-4 w-[200px]" />
</div>
</div>
);
}
51 changes: 51 additions & 0 deletions src/app/sign-up/page.tsx
@@ -0,0 +1,51 @@
import { currentUser } from "@clerk/nextjs";
import { useState } from "react";
import { useForm, type SubmitHandler } from "react-hook-form";
import { Form } from "../_components/form";

interface IPersonalInput {
username: string;
firstName: string;
lastName: string;
yearsAtWork: number;
}

export default async function SignUp() {
// const hello = await api.post.hello.query({ text: "from tRPC" });
const user = await currentUser();
const defaultValues: IPersonalInput = {
username: user?.username ?? "",
firstName: user?.firstName ?? "",
lastName: user?.lastName ?? "",
yearsAtWork: 0,
};

const [loading] = useState<boolean>(false);

const {
handleSubmit,
reset,
register,
formState: { errors },
watch,
} = useForm<IPersonalInput>({
defaultValues,
});

const onSubmit: SubmitHandler<IPersonalInput> = async (data, event) => {
event?.preventDefault();
};

return (
<Form
resetButton
submitText="Update and save text"
handleSubmit={handleSubmit}
reset={reset}
onSubmit={onSubmit}
loading={loading}
>
<div></div>
</Form>
);
}
6 changes: 6 additions & 0 deletions src/app/utils/util.ts
@@ -0,0 +1,6 @@
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";

export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
6 changes: 6 additions & 0 deletions src/lib/utils.ts
@@ -0,0 +1,6 @@
import { type ClassValue, clsx } from "clsx"
import { twMerge } from "tailwind-merge"

export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
15 changes: 13 additions & 2 deletions src/middleware.ts
@@ -1,9 +1,20 @@
import { authMiddleware } from "@clerk/nextjs";
import { authMiddleware, redirectToSignIn } from "@clerk/nextjs";
import { NextResponse } from "next/server";

// This example protects all routes including api/trpc routes
// Please edit this to allow other routes to be public as needed.
// See https://clerk.com/docs/references/nextjs/auth-middleware for more information about configuring your Middleware
export default authMiddleware({});
export default authMiddleware({
afterAuth(auth, req, _evt) {
if (!auth.userId && !auth.isPublicRoute) {
redirectToSignIn({ returnBackUrl: req.url });
}
if (auth.userId && !auth.orgId && req.nextUrl.pathname !== "/sign-up") {
const signUp = new URL("/sign-up", req.url);
return NextResponse.redirect(signUp);
}
},
});

export const config = {
matcher: ["/((?!.+\\.[\\w]+$|_next).*)", "/", "/(api|trpc)(.*)"],
Expand Down

0 comments on commit 47effea

Please sign in to comment.