Skip to content

Commit

Permalink
login wrapper
Browse files Browse the repository at this point in the history
  • Loading branch information
MarcoEscaleira committed Jan 10, 2024
1 parent b5158d2 commit 2717437
Show file tree
Hide file tree
Showing 9 changed files with 168 additions and 9 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,13 @@
"react-map-gl": "^7.1.7",
"react-modal": "^3.16.1",
"react-simple-maps": "^3.0.0",
"react-toastify": "^9.1.3",
"react-tooltip": "^5.25.1",
"yup": "^1.3.3",
"zustand": "^4.4.7"
},
"devDependencies": {
"@redux-devtools/extension": "^3.3.0",
"@types/mapbox-gl": "^2.7.19",
"@types/node": "^20.9.2",
"@types/react": "^18.2.37",
Expand Down
4 changes: 4 additions & 0 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import type { Metadata } from "next";
import { Roboto } from "next/font/google";
import { ToastContainer } from "react-toastify";
import { Footer, Header } from "@/components";
import { ApolloWrapper } from "@/lib/apollo-wrapper";
import "react-toastify/dist/ReactToastify.css";
import "./globals.scss";

const roboto = Roboto({
Expand All @@ -28,6 +30,8 @@ export default function RootLayout({ children }: { children: React.ReactNode })
<Header />
<main className="relative flex h-screen w-screen flex-col">{children}</main>
<Footer />

<ToastContainer />
</ApolloWrapper>
</body>
</html>
Expand Down
43 changes: 40 additions & 3 deletions src/app/profile/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,44 @@
"use client";
import { gql, useLazyQuery } from "@apollo/client";
import { redirect } from "next/navigation";
import { toast } from "react-toastify";
import { useUserStore } from "@/state";

const LOGOUT_USER = gql`
query Query {
logoutUser
}
`;

export default function Profile() {
const { email, firstName, lastName, dateOfBirth, role, createdAt, updatedAt } = useUserStore(state => state.user);

const [makeLogout] = useLazyQuery(LOGOUT_USER);

return (
<main className="flex min-h-screen w-screen items-center justify-center">
<h1 className="text-4xl text-blue-400">User profile</h1>
</main>
<div className="flex min-h-screen w-screen items-center justify-center">
<div className="flex w-72 flex-col">
<h1 className="mb-6 text-4xl text-blue-400">User profile</h1>
<p>Email: {email}</p>
<p>
Full name: {firstName} {lastName}
</p>
<p>DoB: {dateOfBirth}</p>
<p>Role: {role}</p>
<p>Created At: {createdAt}</p>
<p>Updated At: {updatedAt}</p>

<button
onClick={async () => {
await makeLogout();
toast("Logout successful");
redirect("/");
}}
className="mt-12 w-full rounded-lg bg-blue-700 px-5 py-2.5 text-center text-sm font-medium text-white"
>
Logout
</button>
</div>
</div>
);
}
45 changes: 43 additions & 2 deletions src/components/Header/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,61 @@
"use client";
import { useEffect } from "react";
import { gql, useQuery } from "@apollo/client";
import { UserIcon } from "@heroicons/react/24/solid";
import Link from "next/link";
import { LoginModal } from "@/components";
import { useUserStore } from "@/state";

const GET_USER = gql`
query GetMe {
getCurrentlyLoggedInUser {
status
user {
id
email
firstName
lastName
dateOfBirth
role
createdAt
updatedAt
}
}
}
`;

export function Header() {
const { user, setUser } = useUserStore();

const { data, loading, refetch } = useQuery(GET_USER);

useEffect(() => {
if (data?.getCurrentlyLoggedInUser?.status === "success") {
const user = data.getCurrentlyLoggedInUser.user;
setUser({
userId: user.id,
email: user.email,
firstName: user.firstName,
lastName: user.lastName,
dateOfBirth: user.dateOfBirth,
role: user.role,
createdAt: user.createdAt,
updatedAt: user.updatedAt,
});
}
}, [data, loading]);

return (
<>
<header className="absolute left-0 top-0 z-10 w-full px-4 py-2 sm:px-6 sm:py-4">
<div className="flex items-center justify-end">
<Link href="#login">
<Link href={user.userId ? "/profile" : "#login"}>
<UserIcon className="h-10 w-8 cursor-pointer sm:w-10" />
</Link>
</div>
</header>

<LoginModal />
<LoginModal refetchUser={refetch} />
</>
);
}
11 changes: 9 additions & 2 deletions src/components/Login/LoginModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const schema = object().shape({
password: string().required().min(4),
});

export function LoginModal() {
export function LoginModal({ refetchUser }: { refetchUser: () => void }) {
const [isOpen, setIsOpen] = useState(false);
const params = useParams();

Expand All @@ -35,7 +35,7 @@ export function LoginModal() {
resolver: yupResolver(schema),
});

const [loginMutation, { data, loading, error }] = useMutation(LOGIN_USER);
const [loginMutation, { data, loading: isLoadingLogin, error: mutationError }] = useMutation(LOGIN_USER);

useEffect(() => {
if (window.location.hash.includes("login")) {
Expand All @@ -44,6 +44,12 @@ export function LoginModal() {
}
}, [params]);

useEffect(() => {
if (data?.loginUser?.status === "success" && !isLoadingLogin) {
refetchUser();
}
}, [data, isLoadingLogin]);

const loginFormSubmit = handleSubmit(async data => {
try {
await loginMutation({
Expand Down Expand Up @@ -110,6 +116,7 @@ export function LoginModal() {
/>
{errors.password && <p className="text-sm text-red-500">Enter a password.</p>}
</div>
{mutationError?.message && <p className="text-sm text-red-500">{mutationError.message}</p>}
<button
type="submit"
className="w-full rounded-lg bg-blue-700 px-5 py-2.5 text-center text-sm font-medium text-white hover:bg-blue-800 focus:outline-none focus:ring-4 focus:ring-blue-300"
Expand Down
3 changes: 2 additions & 1 deletion src/lib/apollo-wrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"use client";
import { ApolloClient, ApolloLink, HttpLink } from "@apollo/client";
import { ApolloLink, HttpLink } from "@apollo/client";
import {
ApolloNextAppProvider,
NextSSRApolloClient,
Expand All @@ -10,6 +10,7 @@ import {
function makeClient() {
const httpLink = new HttpLink({
uri: "http://localhost:8000/graphql",
credentials: "include",
});

return new NextSSRApolloClient({
Expand Down
1 change: 1 addition & 0 deletions src/state/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./userStore";
46 changes: 46 additions & 0 deletions src/state/userStore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { create } from "zustand";
import { devtools, persist } from "zustand/middleware";
import type {} from "@redux-devtools/extension";

export interface User {
userId: number;
email: string;
firstName: string;
lastName: string;
dateOfBirth: string;
role: string;
createdAt: string;
updatedAt: string;
}

interface UserState {
user: User;
setUser: (user: User) => void;
resetUser: () => void;
}

const defaultUser = {
userId: 0,
email: "",
firstName: "",
lastName: "",
dateOfBirth: "",
role: "",
createdAt: "",
updatedAt: "",
};

export const useUserStore = create<UserState>()(
devtools(
persist(
set => ({
user: defaultUser,
setUser: newUser => set(() => ({ user: newUser })),
resetUser: () => set(() => ({ user: defaultUser })),
}),
{
name: "user-storage",
}
)
)
);
22 changes: 21 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,14 @@
"@nodelib/fs.scandir" "2.1.5"
fastq "^1.6.0"

"@redux-devtools/extension@^3.3.0":
version "3.3.0"
resolved "https://registry.yarnpkg.com/@redux-devtools/extension/-/extension-3.3.0.tgz#bc775d289f15604c472112920beac2cf4dbb7907"
integrity sha512-X34S/rC8S/M1BIrkYD1mJ5f8vlH0BDqxXrs96cvxSBo4FhMdbhU+GUGsmNYov1xjSyLMHgo8NYrUG8bNX7525g==
dependencies:
"@babel/runtime" "^7.23.2"
immutable "^4.3.4"

"@rushstack/eslint-patch@^1.3.3":
version "1.5.1"
resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.5.1.tgz#5f1b518ec5fa54437c0b7c4a821546c64fed6922"
Expand Down Expand Up @@ -820,6 +828,11 @@ [email protected]:
resolved "https://registry.yarnpkg.com/client-only/-/client-only-0.0.1.tgz#38bba5d403c41ab150bff64a95c85013cf73bca1"
integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==

clsx@^1.1.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12"
integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==

color-convert@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
Expand Down Expand Up @@ -1720,7 +1733,7 @@ ignore@^5.2.0:
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.0.tgz#67418ae40d34d6999c95ff56016759c718c82f78"
integrity sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==

immutable@^4.0.0:
immutable@^4.0.0, immutable@^4.3.4:
version "4.3.4"
resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.3.4.tgz#2e07b33837b4bb7662f288c244d1ced1ef65a78f"
integrity sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==
Expand Down Expand Up @@ -2576,6 +2589,13 @@ react-simple-maps@^3.0.0:
d3-zoom "^2.0.0"
topojson-client "^3.1.0"

react-toastify@^9.1.3:
version "9.1.3"
resolved "https://registry.yarnpkg.com/react-toastify/-/react-toastify-9.1.3.tgz#1e798d260d606f50e0fab5ee31daaae1d628c5ff"
integrity sha512-fPfb8ghtn/XMxw3LkxQBk3IyagNpF/LIKjOBflbexr2AWxAH1MJgvnESwEwBn9liLFXgTKWgBSdZpw9m4OTHTg==
dependencies:
clsx "^1.1.1"

react-tooltip@*:
version "5.24.0"
resolved "https://registry.yarnpkg.com/react-tooltip/-/react-tooltip-5.24.0.tgz#c134e4c00d49a111986711696bcd9abf672caff6"
Expand Down

0 comments on commit 2717437

Please sign in to comment.