Skip to content

Commit

Permalink
Merge pull request #278 from chingu-x/refactor/auth-architecture-pt3
Browse files Browse the repository at this point in the history
Refactor/auth architecture pt3
  • Loading branch information
Dan-Y-Ko authored Nov 14, 2024
2 parents b2ca4ab + 8d1f306 commit c05d22c
Show file tree
Hide file tree
Showing 7 changed files with 233 additions and 31 deletions.
1 change: 0 additions & 1 deletion cypress/e2e/authentication/sign-in.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ describe("sign in flow", () => {

cy.contains("button", "Sign In").should("be.visible");
cy.contains("button", "Sign In").click();

cy.contains("Submission Error").should("be.visible");
});
});
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"@heroicons/react": "^2.0.18",
"@hookform/resolvers": "^3.3.1",
"@reduxjs/toolkit": "^1.9.5",
"@tanstack/react-query": "^5.59.20",
"axios": "^1.7.7",
"class-variance-authority": "^0.7.0",
"date-fns": "^3.6.0",
Expand Down Expand Up @@ -50,6 +51,8 @@
"@storybook/nextjs": "^7.6.4",
"@storybook/react": "^7.6.4",
"@storybook/test": "^7.6.4",
"@tanstack/eslint-plugin-query": "^5.59.20",
"@tanstack/react-query-devtools": "^5.59.20",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^14.0.0",
"@types/node": "20.4.5",
Expand Down
45 changes: 35 additions & 10 deletions src/app/(auth)/sign-in/components/SignInFormContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Link from "next/link";
import { useRouter } from "next/navigation";
import { type SubmitHandler, useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { useMutation } from "@tanstack/react-query";
import Button from "@/components/Button";
import TextInput from "@/components/inputs/TextInput";
import { validateTextInput } from "@/utils/form/validateInput";
Expand All @@ -12,6 +13,10 @@ import routePaths from "@/utils/routePaths";
import { type AuthClientAdapter } from "@/modules/auth/adapters/primary/authClientAdapter";
import { TYPES } from "@/di/types";
import { resolve } from "@/di/resolver";
import type { LoginRequestDto } from "@/modules/auth/application/dtos/request.dto";
import type { LoginResponseDto } from "@/modules/auth/application/dtos/response.dto";
import Spinner from "@/components/Spinner";
import { onOpenModal } from "@/store/features/modal/modalSlice";

const validationSchema = z.object({
email: validateTextInput({
Expand Down Expand Up @@ -39,28 +44,48 @@ function SignInFormContainer({
const router = useRouter();
const dispatch = useAppDispatch();

const { mutate, isPending } = useMutation<
LoginResponseDto,
Error,
LoginRequestDto
>({
mutationFn: loginMutation,
onSuccess: () => {
dispatch(clientSignIn());
router.replace(routePaths.dashboardPage());
},
// TODO: update error handling
onError: (error: Error) => {
dispatch(
onOpenModal({ type: "error", content: { message: error.message } }),
);
},
});

const {
register,
formState: { errors },
formState: { errors, isDirty, isValid },
handleSubmit,
} = useForm<ValidationSchema>({
mode: "onTouched",
resolver: zodResolver(validationSchema),
});

// TODO: update error handling
const onSubmit: SubmitHandler<ValidationSchema> = async (data) => {
const { email, password } = data;
async function loginMutation({
email,
password,
}: LoginRequestDto): Promise<LoginResponseDto> {
const authAdapter = resolve<AuthClientAdapter>(TYPES.AuthClientAdapter);
return await authAdapter.login({ email, password });
}

await authAdapter.login({ email, password });

dispatch(clientSignIn());
router.replace(routePaths.dashboardPage());
const onSubmit: SubmitHandler<ValidationSchema> = (data) => {
const { email, password } = data;
mutate({ email, password });
};

function renderButtonContent() {
return "Sign In";
return isPending ? <Spinner /> : "Sign In";
}

return (
Expand Down Expand Up @@ -95,7 +120,7 @@ function SignInFormContainer({
<Button
type="submit"
title="submit"
// disabled={!isDirty || !isValid || serverSignInLoading}
disabled={!isDirty || !isValid || isPending}
>
{renderButtonContent()}
</Button>
Expand Down
7 changes: 5 additions & 2 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Inter } from "next/font/google";
import StoreProvider from "@/components/providers/StoreProvider";
import ThemeProvider from "@/components/providers/ThemeProvider";
import ModalProvider from "@/components/providers/ModalProvider";
import { TanstackQueryProvider } from "@/components/providers/TanstackQueryProvider";

export const metadata: Metadata = {
title: "Chingu Dashboard",
Expand Down Expand Up @@ -33,8 +34,10 @@ export default function RootLayout({
<body className="overflow-hidden">
<ThemeProvider storageKey="chingu-theme" disableTransitionOnChange>
<StoreProvider>
<ModalProvider />
{children}
<TanstackQueryProvider>
<ModalProvider />
{children}
</TanstackQueryProvider>
</StoreProvider>
</ThemeProvider>
</body>
Expand Down
44 changes: 28 additions & 16 deletions src/components/navbar/DropDown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,22 @@ import "reflect-metadata";
import { ChevronDownIcon } from "@heroicons/react/24/outline";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { useMutation } from "@tanstack/react-query";
import Button from "@/components/Button";
import { useAppDispatch, useUser } from "@/store/hooks";
import { clientSignOut } from "@/store/features/auth/authSlice";
import { TYPES } from "@/di/types";
import { resolve } from "@/di/resolver";
import { type AuthClientAdapter } from "@/modules/auth/adapters/primary/authClientAdapter";
import routePaths from "@/utils/routePaths";
import type { LogoutResponseDto } from "@/modules/auth/application/dtos/response.dto";
import { onOpenModal } from "@/store/features/modal/modalSlice";

export default function DropDown({ openState }: { openState?: boolean }) {
interface DropdownProps {
openState?: boolean;
}

export default function DropDown({ openState }: DropdownProps) {
const router = useRouter();
const dispatch = useAppDispatch();
const allVoyages = useUser().voyageTeamMembers;
Expand All @@ -36,24 +43,29 @@ export default function DropDown({ openState }: { openState?: boolean }) {
const open =
"absolute flex flex-col gap-5 z-[1] w-[250px] p-5 bottom-100 translate-y-[15%] shadow-md bg-base-200 right-0 border border-base-100 rounded-2xl";

// TODO: update error handling
async function handleClick() {
const authAdapter = resolve<AuthClientAdapter>(TYPES.AuthClientAdapter);

await authAdapter.logout();
dispatch(clientSignOut());
router.replace(routePaths.signIn());
// if (res) {
// dispatch(clientSignOut());
// }
function handleClick() {
mutate();
}

// if (error) {
// dispatch(
// onOpenModal({ type: "error", content: { message: error.message } }),
// );
// }
async function logoutMutation(): Promise<LogoutResponseDto> {
const authAdapter = resolve<AuthClientAdapter>(TYPES.AuthClientAdapter);
return await authAdapter.logout();
}

const { mutate } = useMutation<LogoutResponseDto, Error, void>({
mutationFn: logoutMutation,
onSuccess: () => {
dispatch(clientSignOut());
router.replace(routePaths.signIn());
},
// TODO: update error handling
onError: (error: Error) => {
dispatch(
onOpenModal({ type: "error", content: { message: error.message } }),
);
},
});

const handleDropDownClick = (event: React.MouseEvent<HTMLDivElement>) => {
event.stopPropagation();
};
Expand Down
22 changes: 22 additions & 0 deletions src/components/providers/TanstackQueryProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
"use client";

import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
import { useState } from "react";

interface TanstackQueryProviderProps {
children: React.ReactNode;
}

export function TanstackQueryProvider({
children,
}: TanstackQueryProviderProps) {
const [queryClient] = useState(() => new QueryClient());

return (
<QueryClientProvider client={queryClient}>
{children}
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
);
}
Loading

0 comments on commit c05d22c

Please sign in to comment.