diff --git a/src/app/(auth)/AuthProvider.tsx b/src/app/(auth)/AuthProvider.tsx index f5b51f2a2..c01ec280c 100644 --- a/src/app/(auth)/AuthProvider.tsx +++ b/src/app/(auth)/AuthProvider.tsx @@ -2,7 +2,7 @@ import { useEffect } from "react"; import { formatInTimeZone } from "date-fns-tz"; -import { clientSignIn, clientSignOut } from "@/store/features/auth/authSlice"; +import { clientSignIn } from "@/store/features/auth/authSlice"; import { useAppDispatch } from "@/store/hooks"; import { type User, getUserState } from "@/store/features/user/userSlice"; import { type AppError } from "@/types/types"; @@ -34,9 +34,9 @@ export default function AuthProvider({ user, error }: AuthProviderProps) { dispatch(getUserState(userWithDate)); } - if (error) { - dispatch(clientSignOut()); - } + // if (error) { + // dispatch(clientSignOut()); + // } }, [dispatch, user, error]); return null; diff --git a/src/app/(auth)/sign-in/components/SignInFormContainer.tsx b/src/app/(auth)/sign-in/components/SignInFormContainer.tsx index 1482b4448..ec419d5b1 100644 --- a/src/app/(auth)/sign-in/components/SignInFormContainer.tsx +++ b/src/app/(auth)/sign-in/components/SignInFormContainer.tsx @@ -9,7 +9,7 @@ import { validateTextInput } from "@/utils/form/validateInput"; import { clientSignIn } from "@/store/features/auth/authSlice"; import { useAppDispatch } from "@/store/hooks"; import routePaths from "@/utils/routePaths"; -import { type AuthClientAdapter } from "@/app/(auth)/_adapters/authClientAdapter"; +import { type AuthClientAdapter } from "@/modules/auth/adapters/primary/authClientAdapter"; import { TYPES } from "@/di/types"; import { resolve } from "@/di/resolver"; @@ -48,6 +48,7 @@ function SignInFormContainer({ resolver: zodResolver(validationSchema), }); + // TODO: update error handling const onSubmit: SubmitHandler = async (data) => { const { email, password } = data; const authAdapter = resolve(TYPES.AuthClientAdapter); diff --git a/src/components/navbar/DropDown.tsx b/src/components/navbar/DropDown.tsx index f3019e541..6ecbed87c 100644 --- a/src/components/navbar/DropDown.tsx +++ b/src/components/navbar/DropDown.tsx @@ -1,44 +1,57 @@ "use client"; +import "reflect-metadata"; import { ChevronDownIcon } from "@heroicons/react/24/outline"; import Link from "next/link"; +import { useRouter } from "next/navigation"; import Button from "@/components/Button"; import { useAppDispatch, useUser } from "@/store/hooks"; import { clientSignOut } from "@/store/features/auth/authSlice"; -import { serverSignOut } from "@/app/(auth)/authService"; -import { onOpenModal } from "@/store/features/modal/modalSlice"; +import { TYPES } from "@/di/types"; +import { resolve } from "@/di/resolver"; +import { type AuthClientAdapter } from "@/modules/auth/adapters/primary/authClientAdapter"; +import routePaths from "@/utils/routePaths"; export default function DropDown({ openState }: { openState?: boolean }) { + const router = useRouter(); const dispatch = useAppDispatch(); const allVoyages = useUser().voyageTeamMembers; const activeVoyage = allVoyages?.find( (item) => item.voyageTeam.voyage.status.name === "Active", ); - const currentVoyage = activeVoyage?.voyageTeam.name - ? `Team - Tier ${activeVoyage.voyageTeam.name + let currentVoyage; + + if (activeVoyage?.voyageTeam.name) { + currentVoyage = `Team - Tier ${activeVoyage.voyageTeam.name .split("-")[1] .split("tier")[1] .toUpperCase()} ${activeVoyage.voyageTeam.name .split("-")[0] - .toUpperCase()}` - : "Please join a voyage to see your status information."; + .toUpperCase()}`; + } else { + currentVoyage = "Please join a voyage to see your status information."; + } const closed = "hidden"; 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 [res, error] = await serverSignOut(); + const authAdapter = resolve(TYPES.AuthClientAdapter); - if (res) { - dispatch(clientSignOut()); - } + await authAdapter.logout(); + dispatch(clientSignOut()); + router.replace(routePaths.signIn()); + // if (res) { + // dispatch(clientSignOut()); + // } - if (error) { - dispatch( - onOpenModal({ type: "error", content: { message: error.message } }), - ); - } + // if (error) { + // dispatch( + // onOpenModal({ type: "error", content: { message: error.message } }), + // ); + // } } const handleDropDownClick = (event: React.MouseEvent) => { diff --git a/src/di/config.ts b/src/di/config.ts index e77ecabef..7fe36201f 100644 --- a/src/di/config.ts +++ b/src/di/config.ts @@ -2,12 +2,14 @@ import { container } from "tsyringe"; import { TYPES } from "./types"; import { AuthApiAdapter } from "@/modules/auth/adapters/secondary/authApiAdapter"; import { AxiosAdapter } from "@/modules/restApi/adapters/secondary/AxiosAdapter"; -import { AuthClientAdapter } from "@/app/(auth)/_adapters/authClientAdapter"; +import { AuthClientAdapter } from "@/modules/auth/adapters/primary/authClientAdapter"; import { LoginUsecase } from "@/modules/auth/application/usecases/loginUsecase"; +import { LogoutUsecase } from "@/modules/auth/application/usecases/logoutUsecase"; container.register(TYPES.RestApiPort, { useClass: AxiosAdapter }); container.register(TYPES.AuthApiPort, { useClass: AuthApiAdapter }); container.register(TYPES.LoginUsecase, { useClass: LoginUsecase }); +container.register(TYPES.LogoutUsecase, { useClass: LogoutUsecase }); container.register(TYPES.AuthClientAdapter, { useClass: AuthClientAdapter }); export default container; diff --git a/src/di/types.ts b/src/di/types.ts index d84a1c8c2..642103a74 100644 --- a/src/di/types.ts +++ b/src/di/types.ts @@ -11,4 +11,5 @@ export const TYPES = { /* UseCases */ LoginUsecase: Symbol.for("LoginUsecase"), + LogoutUsecase: Symbol.for("LogoutUsecase"), }; diff --git a/src/app/(auth)/_adapters/authClientAdapter.ts b/src/modules/auth/adapters/primary/authClientAdapter.ts similarity index 55% rename from src/app/(auth)/_adapters/authClientAdapter.ts rename to src/modules/auth/adapters/primary/authClientAdapter.ts index 0627b15cf..8e9f2847a 100644 --- a/src/app/(auth)/_adapters/authClientAdapter.ts +++ b/src/modules/auth/adapters/primary/authClientAdapter.ts @@ -1,7 +1,11 @@ import { inject, injectable } from "tsyringe"; import { type LoginRequestDto } from "@/modules/auth/application/dtos/request.dto"; -import { type LoginResponseDto } from "@/modules/auth/application/dtos/response.dto"; -import { LoginUsecase } from "@/modules/auth/application/usecases/loginUsecase"; +import type { + LogoutResponseDto, + LoginResponseDto, +} from "@/modules/auth/application/dtos/response.dto"; +import { type LoginUsecase } from "@/modules/auth/application/usecases/loginUsecase"; +import { type LogoutUsecase } from "@/modules/auth/application/usecases/logoutUsecase"; import { type AuthClientPort } from "@/modules/auth/ports/primary/authClientPort"; import { TYPES } from "@/di/types"; @@ -10,9 +14,16 @@ export class AuthClientAdapter implements AuthClientPort { constructor( @inject(TYPES.LoginUsecase) private readonly loginUsecase: LoginUsecase, + + @inject(TYPES.LogoutUsecase) + private readonly logoutUsecase: LogoutUsecase, ) {} async login({ email, password }: LoginRequestDto): Promise { return await this.loginUsecase.execute({ email, password }); } + + async logout(): Promise { + return await this.logoutUsecase.execute(); + } } diff --git a/src/modules/auth/adapters/secondary/authApiAdapter.ts b/src/modules/auth/adapters/secondary/authApiAdapter.ts index 28b2b0df6..31c7b8c9e 100644 --- a/src/modules/auth/adapters/secondary/authApiAdapter.ts +++ b/src/modules/auth/adapters/secondary/authApiAdapter.ts @@ -3,7 +3,10 @@ import { TYPES } from "@/di/types"; import { type AuthApiPort } from "@/modules/auth/ports/secondary/authApiPort"; import { type RestApiPort } from "@/modules/restApi/ports/secondary/restApiPort"; import { type LoginRequestDto } from "@/modules/auth/application/dtos/request.dto"; -import { type LoginResponseDto } from "@/modules/auth/application/dtos/response.dto"; +import type { + LogoutResponseDto, + LoginResponseDto, +} from "@/modules/auth/application/dtos/response.dto"; import { AuthUrls } from "@/modules/auth/application/constants/authUrls"; @injectable() @@ -19,4 +22,10 @@ export class AuthApiAdapter implements AuthApiPort { payload: { email, password }, }); } + + async logout(): Promise { + return await this.apiClient.post({ + url: AuthUrls.logout, + }); + } } diff --git a/src/modules/auth/application/constants/authUrls.ts b/src/modules/auth/application/constants/authUrls.ts index facce7b84..3f4385057 100644 --- a/src/modules/auth/application/constants/authUrls.ts +++ b/src/modules/auth/application/constants/authUrls.ts @@ -1,3 +1,4 @@ export const AuthUrls = { login: "/api/v1/auth/login", + logout: "/api/v1/auth/logout", } as const; diff --git a/src/modules/auth/application/usecases/logoutUsecase.ts b/src/modules/auth/application/usecases/logoutUsecase.ts new file mode 100644 index 000000000..d31c24c64 --- /dev/null +++ b/src/modules/auth/application/usecases/logoutUsecase.ts @@ -0,0 +1,16 @@ +import { inject, injectable } from "tsyringe"; +import { TYPES } from "@/di/types"; +import { type AuthApiPort } from "@/modules/auth/ports/secondary/authApiPort"; +import { type LogoutResponseDto } from "@/modules/auth/application/dtos/response.dto"; + +@injectable() +export class LogoutUsecase { + constructor( + @inject(TYPES.AuthApiPort) + private readonly authApi: AuthApiPort, + ) {} + + async execute(): Promise { + return await this.authApi.logout(); + } +} diff --git a/src/modules/auth/ports/primary/authClientPort.ts b/src/modules/auth/ports/primary/authClientPort.ts index b9ed0e420..610a60859 100644 --- a/src/modules/auth/ports/primary/authClientPort.ts +++ b/src/modules/auth/ports/primary/authClientPort.ts @@ -1,6 +1,10 @@ import { type LoginRequestDto } from "@/modules/auth/application/dtos/request.dto"; -import { type LoginResponseDto } from "@/modules/auth/application/dtos/response.dto"; +import type { + LogoutResponseDto, + LoginResponseDto, +} from "@/modules/auth/application/dtos/response.dto"; export interface AuthClientPort { login: (props: LoginRequestDto) => Promise; + logout: () => Promise; } diff --git a/src/modules/auth/ports/secondary/authApiPort.ts b/src/modules/auth/ports/secondary/authApiPort.ts index fe92c0a15..41393cb90 100644 --- a/src/modules/auth/ports/secondary/authApiPort.ts +++ b/src/modules/auth/ports/secondary/authApiPort.ts @@ -1,6 +1,10 @@ import { type LoginRequestDto } from "@/modules/auth/application/dtos/request.dto"; -import { type LoginResponseDto } from "@/modules/auth/application/dtos/response.dto"; +import type { + LogoutResponseDto, + LoginResponseDto, +} from "@/modules/auth/application/dtos/response.dto"; export interface AuthApiPort { login: ({ email, password }: LoginRequestDto) => Promise; + logout: () => Promise; } diff --git a/src/modules/restApi/ports/secondary/restApiPort.ts b/src/modules/restApi/ports/secondary/restApiPort.ts index eba80fff4..9d6d10d76 100644 --- a/src/modules/restApi/ports/secondary/restApiPort.ts +++ b/src/modules/restApi/ports/secondary/restApiPort.ts @@ -7,13 +7,13 @@ import type { } from "@/modules/restApi/application/entities/restApiParams"; export interface RestApiPort { - get(params: GetParams): Promise; + get: (params: GetParams) => Promise; - post(params: PostParams): Promise; + post: (params: PostParams) => Promise; - patch(params: PatchParams): Promise; + patch: (params: PatchParams) => Promise; - delete(params: DeleteParams): Promise; + delete: (params: DeleteParams) => Promise; - unauthpost(params: UnauthPostParams): Promise; + unauthpost: (params: UnauthPostParams) => Promise; } diff --git a/src/utils/routePaths.ts b/src/utils/routePaths.ts index 554d369be..8c321f58b 100644 --- a/src/utils/routePaths.ts +++ b/src/utils/routePaths.ts @@ -10,9 +10,6 @@ const routePaths = { signIn() { return "/sign-in"; }, - signOut() { - return "/sign-out"; - }, signUp() { return "/sign-up"; },