Skip to content

Commit

Permalink
feat(client): authentication done
Browse files Browse the repository at this point in the history
  • Loading branch information
Ivo committed Apr 20, 2023
1 parent 0fccc0a commit d3a9d1b
Show file tree
Hide file tree
Showing 24 changed files with 891 additions and 72 deletions.
5 changes: 5 additions & 0 deletions apps/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,16 @@
},
"dependencies": {
"@hookform/resolvers": "^3.0.0",
"@next-auth/prisma-adapter": "^1.0.6",
"@prisma/client": "^4.13.0",
"@radix-ui/react-dropdown-menu": "^2.0.4",
"@radix-ui/react-radio-group": "^1.1.2",
"@radix-ui/react-select": "^1.2.1",
"@radix-ui/react-switch": "^1.0.2",
"@tailwindcss/forms": "^0.5.3",
"@tanstack/react-query": "^4.29.1",
"@tanstack/react-query-devtools": "^4.29.1",
"argon2": "^0.30.3",
"axios": "^1.3.4",
"clsx": "^1.2.1",
"contentlayer": "^0.3.1",
Expand All @@ -31,6 +34,7 @@
"i18next": "^22.4.14",
"lucide-react": "^0.127.0",
"next": "13.3.0",
"next-auth": "^4.22.1",
"next-contentlayer": "^0.3.1",
"next-i18next": "^13.2.2",
"next-sitemap": "^4.0.6",
Expand Down Expand Up @@ -64,6 +68,7 @@
"identity-obj-proxy": "^3.0.0",
"postcss": "^8.4.19",
"prettier": "^2.8.4",
"prisma": "^4.13.0",
"rehype-autolink-headings": "^6.1.1",
"rehype-slug": "^5.1.0",
"remark-breaks": "^3.0.2",
Expand Down
7 changes: 7 additions & 0 deletions apps/client/prisma/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Environment variables declared in this file are automatically made available to Prisma.
# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema

# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.
# See the documentation for all the connection string options: https://pris.ly/d/connection-strings

DATABASE_URL="postgresql://postgres:postgres@localhost:5432/visualdynamics"
23 changes: 23 additions & 0 deletions apps/client/prisma/schema.prisma
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}

generator client {
provider = "prisma-client-js"
}

enum Role {
ADMIN
USER
}

model User {
id String @id @default(cuid())
email String @unique
username String @unique
password String
name String?
active Boolean @default(false)
role Role @default(USER)
}
24 changes: 24 additions & 0 deletions apps/client/public/locales/en-US/navigation.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
{
"auth": {
"role": {
"admin": "Administrator",
"user": "User"
}
},
"dynamic": {
"my-dynamics": "My Dynamics",
"title": "Dynamics",
Expand All @@ -7,6 +13,24 @@
"acpype": "ACPYPE with Amber"
}
},
"forms": {
"identifier": {
"title": "Email or Username",
"placeholder": "user@uni.edu",
"errors": {
"empty": "Can't be empty"
}
},
"password": {
"title": "Password",
"placeholder": "********",
"errors": {
"empty": "Can't be empty"
}
},
"submit": "Sign In",
"lost-password": "I forgot my password"
},
"preparation": {
"title": "Dynamics Preparation",
"models": {
Expand Down
8 changes: 8 additions & 0 deletions apps/client/src/@types/i18next.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// i18next.d.ts
import "i18next";

declare module "i18next" {
interface CustomTypeOptions {
returnNull: false;
}
}
17 changes: 17 additions & 0 deletions apps/client/src/@types/next-auth.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { User as PUser } from "@prisma/client";
import NextAuth, { DefaultSession } from "next-auth";

declare module "next-auth" {
interface Session extends DefaultSession {
user: Omit<PUser, "password">;
}
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface User extends Omit<PUser, "password"> {}
}

declare module "next-auth/jwt" {
/** Returned by the `jwt` callback and `getToken`, when using JWT sessions */
interface JWT {
user?: Omit<PUser, "password">;
}
}
110 changes: 110 additions & 0 deletions apps/client/src/components/Auth/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { SubmitHandler, useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { Lock, LogIn, LogOut, User } from "lucide-react";
import { signIn, signOut, useSession } from "next-auth/react";
import { useTranslation } from "next-i18next";

import {
AuthFormSchema,
AuthFormSchemaType
} from "@app/schemas/components/auth/auth.zod";

import { Button } from "../Button";
import { TextButton } from "../Button/Text";
import { Input } from "../Input";

export function Auth() {
const { data: session, status } = useSession();
const {
register,
reset,
formState: { errors },
handleSubmit
} = useForm<AuthFormSchemaType>({
resolver: zodResolver(AuthFormSchema)
});
const { t } = useTranslation(["navigation"]);

const handleAuth: SubmitHandler<AuthFormSchemaType> = async ({
identifier,
password
}) => {
signIn("credentials", {
identifier,
password,
redirect: false
}).then(() => reset());
};

if (status === "authenticated") {
return (
<div className="flex w-full gap-x-2">
<User className="stroke-primary-950 my-auto h-7 w-7" />
<div className="flex flex-col gap-y-1 flex-1">
<div className="flex gap-x-1">
<p className="uppercase -mb-2 text-sm text-primary-950">
{session.user.username}
</p>
<small className="text-xs -mb-2 text-zinc-500">
{t(`navigation:auth.role.${session.user.role.toLowerCase()}`)}
</small>
</div>
<small className="text-xs text-zinc-500">{session.user.email}</small>
</div>
<TextButton
LeftIcon={LogOut}
onClick={() =>
signOut({
redirect: false
})
}
/>
</div>
);
}

if (status === "loading") {
return <h1>loading</h1>;
}

return (
<>
<div className="flex gap-x-1.5 w-full -mb-4 text-primary-950 transition-all duration-500">
<LogIn className="h-5 w-5 my-auto stroke-[3]" />
<h3 className="font-medium text-xl">{t("navigation:forms.submit")}</h3>
</div>
<form
className="flex flex-col w-full gap-y-2.5"
onSubmit={handleSubmit(handleAuth)}
>
<Input
error={errors.identifier}
label={t("navigation:forms.identifier.title")}
placeholder={t("navigation:forms.identifier.placeholder")}
{...register("identifier")}
/>
<Input
error={errors.password}
label={t("navigation:forms.password.title")}
placeholder={t("navigation:forms.password.placeholder")}
type="password"
{...register("password")}
/>
<Button
LeftIcon={LogIn}
type="submit"
>
{t("navigation:forms.submit")}
</Button>
<TextButton
className="text-sm"
iconClassName="h-4 w-4"
LeftIcon={Lock}
type="button"
>
{t("navigation:forms.lost-password")}
</TextButton>
</form>
</>
);
}
2 changes: 1 addition & 1 deletion apps/client/src/components/Button/Text.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const TextButton: FC<ButtonProps> = ({
}) => {
return (
<button
className={`p-2 font-bold font-inter items-center justify-center text-primary-500 flex gap-x-2 disabled:opacity-60 enabled:hover:text-primary-600 transition-all duration-500 rounded-md outline-none focus:ring-2 focus:ring-primary-400 focus:ring-offset-2 focus:ring-offset-zinc-200 ${className}`}
className={`px-2 font-bold font-inter items-center justify-center text-primary-500 flex gap-x-1 disabled:opacity-60 enabled:hover:text-primary-600 transition-all duration-500 rounded-md outline-none focus:ring-2 focus:ring-primary-400 focus:ring-offset-2 focus:ring-offset-zinc-200 ${className}`}
{...rest}
>
{LeftIcon ? <LeftIcon className={iconClassName} /> : null}
Expand Down
2 changes: 1 addition & 1 deletion apps/client/src/components/Input/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
<div className="flex gap-x-2.5">
<label htmlFor={name}>{label}</label>
{error ? (
<p className="font-grotesk text-red-600 text-sm mt-auto">
<p className="font-grotesk text-red-600 text-sm my-auto">
{t(error.message ?? "")}
</p>
) : null}
Expand Down

0 comments on commit d3a9d1b

Please sign in to comment.