Skip to content

Commit

Permalink
[RSN-72] - Login/register logic (#69)
Browse files Browse the repository at this point in the history
* feat: add login and register logic to components

TODO: Add same logic to organizer registration.

* feat: changed register page styling, added error handling

* chore: linting

---------

Co-authored-by: raczu <[email protected]>
  • Loading branch information
wzarek and raczu authored Jun 23, 2024
1 parent 94d0f6a commit 656a913
Show file tree
Hide file tree
Showing 20 changed files with 891 additions and 460 deletions.
1 change: 1 addition & 0 deletions Client/reasn-client/apps/web/.env.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
REASN_API_URL=https://localhost:5272
40 changes: 40 additions & 0 deletions Client/reasn-client/apps/web/app/login/action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"use server";

import { LoginRequestSchema } from "@reasn/common/src/schemas/LoginRequest";
import { login } from "@/services/auth";
import { setToken } from "@/lib/token";
import { redirect } from "next/navigation";
import {
formatZodError,
handleErrorMessage,
} from "@reasn/common/src/helpers/errorHelpers";

export type LoginFormState = {
message?: string | null;
errors?: string | {};
};

export const loginAction = async (
prevState: any,
formData: FormData,
): Promise<LoginFormState | undefined> => {
const result = LoginRequestSchema.safeParse({
email: formData.get("email"),
password: formData.get("password"),
});

if (!result.success) {
return {
errors: formatZodError(result.error),
message: "Niepoprawne wartości formularza.",
};
}

try {
const payload = await login(result.data);
setToken(payload);
redirect("/");
} catch (e) {
return handleErrorMessage(e);
}
};
40 changes: 34 additions & 6 deletions Client/reasn-client/apps/web/app/login/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,40 @@ import {
FloatingInput,
} from "@reasn/ui/src/components/shared/form";
import Link from "next/link";
import React, { useRef } from "react";
import React, { useEffect, useRef, useState } from "react";
import { useFormState } from "react-dom";
import { LoginFormState, loginAction } from "@/app/login/action";
import { Toast } from "@reasn/ui/src/components/shared";

const LoginPage = () => {
const initialState = { message: null, errors: {} };
const formRef = useRef<HTMLFormElement>(null);
const [state, formAction] = useFormState(loginAction, initialState);
const [error, setError] = useState<LoginFormState>({});

const handleFormSubmit = () => {
console.log("form submitted");
formRef.current?.submit();
const handleFormAction = async (formData: FormData) => {
const res = await loginAction(state, formData);
if (res?.message) {
setError(res);
}
};

useEffect(() => {
if (error?.message) {
setTimeout(() => {
setError({});
}, 5000);
}
}, [error]);

return (
<>
<div className="relative z-10 flex w-full items-center justify-center sm:w-1/2">
<form className="flex w-full flex-col gap-2 sm:gap-8" ref={formRef}>
<form
className="flex w-full flex-col gap-2 sm:gap-8"
ref={formRef}
action={handleFormAction}
>
<FloatingInput type="email" label="email" name="email" />
<FloatingInput type="password" label="hasło" name="password" />
<div className="flex justify-end gap-2 text-sm">
Expand All @@ -33,9 +53,17 @@ const LoginPage = () => {
<p className="bg-gradient-to-r from-[#FF6363] to-[#1E34FF] bg-clip-text text-right text-4xl font-bold leading-tight text-transparent sm:text-left lg:text-5xl">
miło, że do nas wracasz
</p>
<ButtonBase text={"zaloguj"} onClick={handleFormSubmit} />
<ButtonBase
text={"zaloguj"}
onClick={() => formRef.current?.requestSubmit()}
/>
</div>
<div className="absolute right-[-50%] top-0 z-0 h-full w-4/5 rounded-full bg-[#000b6d] opacity-15 blur-3xl"></div>
<div className="absolute right-10 top-10">
{error?.message && (
<Toast message={error.message} errors={error.errors as string} />
)}
</div>
</>
);
};
Expand Down
62 changes: 62 additions & 0 deletions Client/reasn-client/apps/web/app/register/action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
"use server";

import {
RegisterRequestSchema,
RegisterRequestMapper,
} from "@reasn/common/src/schemas/RegisterRequest";
import { register } from "@/services/auth";
import { redirect } from "next/navigation";
import { z } from "zod";
import {
formatZodError,
handleErrorMessage,
} from "@reasn/common/src/helpers/errorHelpers";

export type RegisterFormState = {
message?: string | null;
errors?: string | {};
};

const RegisterRequestExtendedSchema = RegisterRequestSchema.extend({
confirmPassword: z.string(),
}).refine((data) => data.password === data.confirmPassword);

export type RegisterFormData = z.infer<typeof RegisterRequestExtendedSchema>;

export const registerAction = async (
prevState: any,
formData: FormData,
): Promise<RegisterFormState | undefined> => {
const result = RegisterRequestExtendedSchema.safeParse({
name: formData.get("name"),
surname: formData.get("surname"),
email: formData.get("email"),
username: formData.get("username"),
password: formData.get("password"),
confirmPassword: formData.get("confirmPassword"),
phone: formData.get("phone"),
address: {
country: formData.get("country"),
city: formData.get("city"),
street: formData.get("street"),
state: formData.get("state"),
zipCode: formData.get("postcode"),
},
role: formData.get("role"),
});

if (!result.success) {
console.log(result.error);
return {
errors: formatZodError(result.error),
message: "Niepoprawne wartości formularza.",
};
}

try {
await register(RegisterRequestMapper.fromObject(result.data));
redirect("/login");
} catch (e) {
return handleErrorMessage(e);
}
};
Loading

0 comments on commit 656a913

Please sign in to comment.