Skip to content

Commit

Permalink
Merge pull request #7 from Fingertips18/development
Browse files Browse the repository at this point in the history
Add user info display on the root page for signed in users
  • Loading branch information
Fingertips18 authored Sep 25, 2024
2 parents f7b3d8b + cfd692a commit cca8905
Show file tree
Hide file tree
Showing 24 changed files with 188 additions and 42 deletions.
Binary file added client/src/assets/wave.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 5 additions & 1 deletion client/src/components/switch-auth.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@ const SwitchAuth = ({ label, tag, href, disabled }: SwitchAuthProps) => {
to={href}
replace
className={`font-bold underline-offset-4 hover:underline transition-all hover:drop-shadow-secondary-glow
${disabled} ? "text-secondary/50 pointer-events-none" : "text-secondary"
${
disabled
? "text-secondary/50 pointer-events-none"
: "text-secondary"
}
`}
>
{tag}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
interface AuthTitleProps {
interface TitleProps {
title: string;
body?: string;
}

const AuthTitle = ({ title, body }: AuthTitleProps) => {
const Title = ({ title, body }: TitleProps) => {
return (
<div className="w-full rounded-md border border-primary/50 bg-primary/15 drop-shadow-2xl">
<h2
Expand All @@ -24,4 +24,4 @@ const AuthTitle = ({ title, body }: AuthTitleProps) => {
);
};

export { AuthTitle };
export { Title };
2 changes: 2 additions & 0 deletions client/src/constants/assets.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import wave from "@/assets/wave.gif";
import key from "@/assets/key.svg";

export const KEY = key;
export const WAVE = wave;
4 changes: 2 additions & 2 deletions client/src/constants/collections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export const SIGNIN_INPUTS = [

export const RESET_PASSWORD_INPUTS = [
{
name: "oldPassword",
name: "old-password",
label: "Old Password",
tooltip: "Enter your current password",
placeholder: "e.g. m#P52s@ap$V",
Expand All @@ -96,7 +96,7 @@ export const RESET_PASSWORD_INPUTS = [
maxLength: 128,
},
{
name: "newPassword",
name: "new-password",
label: "New Password",
tooltip:
"Create a password with at least 8 characters, including uppercase, lowercase, numbers, and special characters for security",
Expand Down
4 changes: 2 additions & 2 deletions client/src/lib/DTO/reset-dto.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export type ResetDTO = {
token: string;
oldpassword: string;
newpassword: string;
old_password: string;
new_password: string;
};
12 changes: 6 additions & 6 deletions client/src/lib/services/auth-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { UserResponse } from "@/lib/classes/user-response-class";
import { SignUpDTO } from "@/lib/DTO/sign-up-dto";
import { SignInDTO } from "@/lib/DTO/sign-in-dto";
import { AppRoutes } from "@/constants/routes";
import { ResetDTO } from "../DTO/reset-dto";
import { ResetDTO } from "@/lib/DTO/reset-dto";

const baseURL =
import.meta.env.VITE_ENV === "development"
Expand All @@ -28,9 +28,8 @@ export const AuthService = {
});
}

return new UserResponse({
return new GenericResponse({
message: data.message,
user: data.user,
});
},
signIn: async (signIn: SignInDTO) => {
Expand All @@ -52,8 +51,9 @@ export const AuthService = {
});
}

return new GenericResponse({
return new UserResponse({
message: data.message,
user: data.user,
});
},
signOut: async () => {
Expand Down Expand Up @@ -172,8 +172,8 @@ export const AuthService = {
"Content-Type": "application/json",
},
body: JSON.stringify({
old_password: reset.oldpassword,
new_password: reset.newpassword,
old_password: reset.old_password,
new_password: reset.new_password,
}),
}
);
Expand Down
24 changes: 24 additions & 0 deletions client/src/lib/stores/user-store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { createJSONStorage, devtools, persist } from "zustand/middleware";
import { create } from "zustand";

import { UserDTO } from "@/lib/DTO/user-dto";

interface UserStoreState {
user?: UserDTO;
setUser: (user?: UserDTO) => void;
}

export const useUserStore = create<UserStoreState>()(
devtools(
persist(
(set) => ({
user: undefined,
setUser: (user?: UserDTO) => set({ user }),
}),
{
name: "go-react-user",
storage: createJSONStorage(() => sessionStorage),
}
)
)
);
16 changes: 16 additions & 0 deletions client/src/lib/utils/date.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export const formatDate = (dateString: string) => {
const date = new Date(dateString);

if (isNaN(date.getTime())) {
return "Invalid Date";
}

return date.toLocaleString("en-US", {
year: "numeric",
month: "short",
day: "numeric",
hour: "2-digit",
minute: "2-digit",
hour12: true,
});
};
4 changes: 2 additions & 2 deletions client/src/pages/forgot-password/page.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { AuthTitle } from "@/components/auth-title";
import { Title } from "@/components/title";

import { ForgotPasswordForm } from "./_components/forgot-password-form";
import { ForgotPasswordBack } from "./_components/forgot-password-back";

const ForgotPasswordPage = () => {
return (
<section className="px-4 lg:px-0 h-full flex-center flex-col gap-y-6 w-fit mx-auto">
<AuthTitle
<Title
title="Forgot Password"
body="Enter your email address and wait for a reset password link to be sent."
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,13 @@ const ResetPasswordForm = () => {

const formData = new FormData(e.currentTarget);

const resetPasswordData = Object.fromEntries(
formData.entries()
) as ResetDTO;
const resetPasswordData: ResetDTO = {
token: token,
old_password: formData.get("old-password") as string,
new_password: formData.get("new-password") as string,
};

resetPasswordData.token = token;
console.log(resetPasswordData);

setGlobalLoading(true);

Expand Down
4 changes: 2 additions & 2 deletions client/src/pages/reset-password/page.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { AuthTitle } from "@/components/auth-title";
import { Title } from "@/components/title";

import { ResetPasswordForm } from "./_components/reset-password-form";
import { ResetPasswordBack } from "./_components/reset-password-back";

const ResetPasswordPage = () => {
return (
<section className="px-4 lg:px-0 h-full flex-center flex-col gap-y-6 w-fit mx-auto">
<AuthTitle title="Reset Password" />
<Title title="Reset Password" />

<ResetPasswordForm />

Expand Down
36 changes: 36 additions & 0 deletions client/src/pages/root/_components/content/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { useUserStore } from "@/lib/stores/user-store";
import { formatDate } from "@/lib/utils/date";

import { WelcomeUser } from "./welcome-user";
import { InfoPair } from "./info-pair";

const Content = () => {
const { user } = useUserStore();

return (
<section className="h-[calc(100dvh_-_56px)] flex-center max-w-screen-lg mx-auto px-4 lg:px-0">
<div className="w-fit space-y-6">
<WelcomeUser name={user?.username} />

<div className="w-full rounded-md border border-secondary/50 bg-secondary/15 drop-shadow-2xl">
<InfoPair label="Email" value={user?.email_address} />
<InfoPair
label="Last Visit"
value={formatDate(new Date(user!.last_signed_in).toDateString())}
/>
<InfoPair
label="Joined"
value={new Date(user!.created_at).toLocaleDateString("en-US", {
year: "numeric",
month: "long",
day: "numeric",
})}
/>
<InfoPair label="Verified" value={user!.is_verified ? "Yes" : "No"} />
</div>
</div>
</section>
);
};

export { Content };
14 changes: 14 additions & 0 deletions client/src/pages/root/_components/content/info-pair.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
interface InfoPairProps {
label: string;
value?: string;
}

const InfoPair = ({ label, value }: InfoPairProps) => {
return (
<p className="font-medium flex items-center gap-x-2 px-6 py-4 border-b border-secondary/50">
{label}:<span className="font-semibold text-secondary">{value}</span>
</p>
);
};

export { InfoPair };
29 changes: 29 additions & 0 deletions client/src/pages/root/_components/content/welcome-user.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { WAVE } from "@/constants/assets";

interface WelcomeUserProps {
name?: string;
}

const WelcomeUser = ({ name }: WelcomeUserProps) => {
return (
<div className="w-full rounded-md border border-primary/50 bg-primary/15 drop-shadow-2xl">
<h2
className="text-lg lg:text-2xl font-extrabold flex-center uppercase
text-primary drop-shadow-primary-glow animate-pulse p-4 gap-x-4"
>
<span className="text-foreground flex-center gap-x-4 relative">
Hi there{" "}
<img src={WAVE} alt="wave" className="size-8 relative -top-[3px]" />
</span>
{name}
</h2>
<div className="w-full bg-primary/25 p-2.5 flex-center border-t border-primary/50">
<p className="text-xs lg:text-base text-center lg:max-w-[324px] text-foreground/60 font-semibold">
Welcome to Go + React Auth
</p>
</div>
</div>
);
};

export { WelcomeUser };
11 changes: 11 additions & 0 deletions client/src/pages/root/_components/footer/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const Footer = () => {
return (
<footer className="bg-secondary/10 backdrop-blur-lg border-t border-secondary/25 p-4">
<p className="max-w-screen-lg mx-auto text-center text-sm font-medium text-foreground/60">
&copy; {new Date().getUTCFullYear()} Fingertips. All rights reserved.
</p>
</footer>
);
};

export { Footer };
3 changes: 3 additions & 0 deletions client/src/pages/root/_components/header/sign-out-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,21 @@ import { toast } from "sonner";

import { GenericResponse } from "@/lib/classes/generic-response-class";
import { AuthService } from "@/lib/services/auth-service";
import { useUserStore } from "@/lib/stores/user-store";
import { useAuthStore } from "@/lib/stores/auth-store";
import IconButton from "@/components/icon-button";
import { SIGNOUTKEY } from "@/constants/keys";

const SignOutButton = () => {
const { setAuthorized } = useAuthStore();
const { setUser } = useUserStore();

const { mutate, isPending } = useMutation({
mutationKey: [SIGNOUTKEY],
mutationFn: AuthService.signOut,
onSuccess: (res: GenericResponse) => {
toast.success(res.message);
setUser(undefined);
setAuthorized(false);
},
onError: ({ message }) => toast.error(message),
Expand Down
7 changes: 4 additions & 3 deletions client/src/pages/root/page.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { Footer } from "./_components/footer";
import { Header } from "./_components/header";
import { Content } from "./_components/content";

const RootPage = () => {
return (
<>
<Header />
<section className="h-full flex-center">
<h1 className="font-bold text-2xl">Root Page</h1>
</section>
<Content />
<Footer />
</>
);
};
Expand Down
9 changes: 7 additions & 2 deletions client/src/pages/sign-in/_components/sign-in-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ import { useMutation } from "@tanstack/react-query";
import { FormEvent } from "react";
import { toast } from "sonner";

import { GenericResponse } from "@/lib/classes/generic-response-class";
import { ErrorResponse } from "@/lib/classes/error-response-class";
import { UserResponse } from "@/lib/classes/user-response-class";
import { AuthService } from "@/lib/services/auth-service";
import { SIGNIN_INPUTS } from "@/constants/collections";
import { useAuthStore } from "@/lib/stores/auth-store";
import { useUserStore } from "@/lib/stores/user-store";
import { SignInDTO } from "@/lib/DTO/sign-in-dto";
import { Button } from "@/components/text-button";
import { AppRoutes } from "@/constants/routes";
Expand All @@ -21,22 +22,26 @@ const SignInForm = () => {
setAuthorized,
setLoading: setGlobalLoading,
} = useAuthStore();
const { setUser } = useUserStore();

const { mutate, isPending } = useMutation({
mutationKey: [SIGNINKEY],
mutationFn: AuthService.signIn,
onSuccess: (res: GenericResponse) => {
onSuccess: (res: UserResponse) => {
toast.success(res.message);
setUser(res.user);
setAuthorized(true);
setGlobalLoading(false);
},
onError: (error: ErrorResponse) => {
if (error.status == 403) {
toast.error("Please verify to sign in");
setUser(undefined);
setGlobalLoading(false);
navigate(AppRoutes.VerifyEmail);
} else {
toast.error(error.message);
setUser(undefined);
setAuthorized(false);
setGlobalLoading(false);
}
Expand Down
4 changes: 2 additions & 2 deletions client/src/pages/sign-in/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AuthTitle } from "@/components/auth-title";
import { Title } from "@/components/title";
import { Or } from "@/components/or";

import { NoAccountYet } from "./_components/no-account-yet";
Expand All @@ -7,7 +7,7 @@ import { SignInForm } from "./_components/sign-in-form";
const SignInPage = () => {
return (
<section className="px-4 lg:px-0 h-full flex-center flex-col gap-y-6 w-fit mx-auto">
<AuthTitle title="Access Your Account" />
<Title title="Access Your Account" />

<SignInForm />

Expand Down
Loading

0 comments on commit cca8905

Please sign in to comment.