From 95674a489c054751b4243670c5fc343330957915 Mon Sep 17 00:00:00 2001 From: ariari04 Date: Tue, 10 Sep 2024 15:02:17 +0600 Subject: [PATCH] Add profile page --- src/app/[locale]/news/[id]/page.tsx | 4 +-- src/app/[locale]/profile/layout.tsx | 45 ++++++++++++++++++++++++ src/app/[locale]/profile/page.tsx | 13 +++++++ src/features/LogoutButton.tsx | 29 ++++++++++++++++ src/widgets/Navbar/NavAuth/NavAuth.tsx | 36 ++++++++------------ src/widgets/Navbar/Navbar.tsx | 2 +- src/widgets/ProfileNav/ProfileNav.tsx | 47 ++++++++++++++++++++++++++ 7 files changed, 150 insertions(+), 26 deletions(-) create mode 100644 src/app/[locale]/profile/layout.tsx create mode 100644 src/app/[locale]/profile/page.tsx create mode 100644 src/features/LogoutButton.tsx create mode 100644 src/widgets/ProfileNav/ProfileNav.tsx diff --git a/src/app/[locale]/news/[id]/page.tsx b/src/app/[locale]/news/[id]/page.tsx index 3aeb96c..89f54c6 100644 --- a/src/app/[locale]/news/[id]/page.tsx +++ b/src/app/[locale]/news/[id]/page.tsx @@ -1,8 +1,6 @@ /* eslint-disable @next/next/no-img-element */ import { apiInstance } from "@/shared/config/apiConfig"; -import { INews, INewsData, NewsDataResponse } from "@/shared/types/news-type"; -import Image from "next/image"; -import calendar from "./icons/calendar.svg"; +import { INews, NewsDataResponse } from "@/shared/types/news-type"; import { Metadata } from "next"; import { Container, Title } from "@/shared/ui"; diff --git a/src/app/[locale]/profile/layout.tsx b/src/app/[locale]/profile/layout.tsx new file mode 100644 index 0000000..f2517f6 --- /dev/null +++ b/src/app/[locale]/profile/layout.tsx @@ -0,0 +1,45 @@ +import { AxiosError } from "axios"; +import { apiInstance } from "@/shared/config/apiConfig"; +import { getServerSession } from "next-auth"; +import { authConfig } from "@/shared/config/authConfig"; +import { Title } from "@/shared/ui"; +import ProfileNav from "@/widgets/ProfileNav/ProfileNav"; + +const Profile = async ({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) => { + const session = await getServerSession(authConfig); + + const getProfile = async () => { + const Authorization = `Bearer ${session?.access_token}`; + const config = { + headers: { + Authorization, + }, + }; + try { + const response = await apiInstance.get<{ + report_count: number; + }>("/users/profile/", config); + + return response.data; + } catch (error: unknown) { + if (error instanceof AxiosError) console.log(error.message); + } + }; + + const data = await getProfile(); + + return ( +
+ + <ProfileNav report_count={data?.report_count as number} /> + + {children} + </div> + ); +}; + +export default Profile; diff --git a/src/app/[locale]/profile/page.tsx b/src/app/[locale]/profile/page.tsx new file mode 100644 index 0000000..a4d9918 --- /dev/null +++ b/src/app/[locale]/profile/page.tsx @@ -0,0 +1,13 @@ +"use client"; +import { useRouter } from "@/shared/config/navigation"; +import { useEffect } from "react"; + +const Profile = () => { + const router = useRouter(); + useEffect(() => { + router.push("/profile/personal"); + }, []); + return <></>; +}; + +export default Profile; diff --git a/src/features/LogoutButton.tsx b/src/features/LogoutButton.tsx new file mode 100644 index 0000000..4951d76 --- /dev/null +++ b/src/features/LogoutButton.tsx @@ -0,0 +1,29 @@ +import { cn } from "@/lib/utils"; +import { signOut } from "next-auth/react"; + +interface ILogoutButtonProps { + className?: string; +} + +const LogoutButton: React.FC<ILogoutButtonProps> = ({ + className, +}: ILogoutButtonProps) => { + return ( + <button + type="button" + className={cn( + "p-[10px] border border-red-500 rounded-sm font-semibold leading-5 text-red-500", + className + )} + onClick={() => + signOut({ + callbackUrl: "/", + }) + } + > + Выйти из аккаунта + </button> + ); +}; + +export default LogoutButton; diff --git a/src/widgets/Navbar/NavAuth/NavAuth.tsx b/src/widgets/Navbar/NavAuth/NavAuth.tsx index 162f5f9..12cd56c 100644 --- a/src/widgets/Navbar/NavAuth/NavAuth.tsx +++ b/src/widgets/Navbar/NavAuth/NavAuth.tsx @@ -1,6 +1,5 @@ -import { usePathname } from "next/navigation"; import { useSession } from "next-auth/react"; -import { Link } from "@/shared/config/navigation"; +import { Link, usePathname } from "@/shared/config/navigation"; import { useTranslations } from "next-intl"; interface INavAuthProps { @@ -8,29 +7,30 @@ interface INavAuthProps { setOpenMenu: (open: boolean) => void; } -const NavAuth: React.FC<INavAuthProps> = ({ - responsible, - setOpenMenu, -}: INavAuthProps) => { +const NavAuth: React.FC<INavAuthProps> = ({ responsible, setOpenMenu }) => { const t = useTranslations("navigation"); const session = useSession(); - const auth = session.status === "authenticated" ? true : false; - + const auth = session.status === "authenticated"; const pathname = usePathname(); const isActiveProfile = pathname === "/profile"; const isActiveSignIn = pathname === "/sign-in"; + const linkStyles = (isActive: boolean) => + `w-fit px-5 py-3 border-3 rounded-lg font-semibold transition-colors duration-300 ${ + responsible + ? isActive + ? "bg-[#3695d8] text-white" + : "bg-white text-[#3695d8] border-[#3695d8]" + : "bg-[#3695d8] text-white border-[#3695d8]" + }`; + return ( <> {auth ? ( <Link onClick={() => setOpenMenu(false)} href="/profile/personal" - className={`${ - responsible - ? `w-fit px-5 py-3 border-3 rounded-lg font-semibold transition-colors duration-300 ${"bg-[#3695d8] text-white"}` - : "w-fit px-5 py-3 border-3 rounded-lg font-semibold transition-colors duration-300 bg-[#3695d8] text-white border-[#3695d8]" - }`} + className={linkStyles(isActiveProfile)} > {t("profile")} </Link> @@ -38,15 +38,7 @@ const NavAuth: React.FC<INavAuthProps> = ({ <Link onClick={() => setOpenMenu(false)} href="/sign-in" - className={`${ - responsible - ? `w-fit px-5 py-3 border-3 rounded-lg font-semibold transition-colors duration-300 ${ - isActiveSignIn - ? "bg-[#3695d8] text-white" - : "bg-white text-[#3695d8] border-[#3695d8]" - }` - : "w-fit px-5 py-3 border-3 rounded-lg font-semibold transition-colors duration-300 bg-[#3695d8] text-white" - }`} + className={linkStyles(isActiveSignIn)} > {t("login")} </Link> diff --git a/src/widgets/Navbar/Navbar.tsx b/src/widgets/Navbar/Navbar.tsx index 253093e..755af74 100644 --- a/src/widgets/Navbar/Navbar.tsx +++ b/src/widgets/Navbar/Navbar.tsx @@ -48,7 +48,7 @@ const Navbar = () => { {LINKS().map((link) => ( <Link className={`text-black opacity-0.5 size-4 font-normal min-w-[150px] flex justify-center${ - pathname === link.pathname ? " opacity-1 font-extrabold" : "" + pathname === link.pathname ? "opacity-1 font-bold" : "" }`} key={link.id} href={link.pathname} diff --git a/src/widgets/ProfileNav/ProfileNav.tsx b/src/widgets/ProfileNav/ProfileNav.tsx new file mode 100644 index 0000000..ba8db16 --- /dev/null +++ b/src/widgets/ProfileNav/ProfileNav.tsx @@ -0,0 +1,47 @@ +"use client"; + +import LogoutButton from "@/features/LogoutButton"; +import { Link, usePathname } from "@/shared/config/navigation"; + +interface IProfileNavProps { + report_count?: number; +} + +const ProfileNav: React.FC<IProfileNavProps> = ({ + report_count, +}: IProfileNavProps) => { + const pathname = usePathname(); + return ( + <nav className="mb-[55px] flex items-center justify-between"> + <div className="flex items-center"> + <Link + id={pathname === "/profile/personal" ? "profile-nav__link" : ""} + href="/profile/personal" + className="mr-[46px] text-[20px] font-semibold leading-6 text-gray-500" + > + Личные данные + </Link> + <Link + id={pathname === "/profile/my-reports" ? "profile-nav__link" : ""} + href="/profile/my-reports" + className="text-[20px] font-semibold leading-6 text-gray-500" + > + Мои обращения + </Link> + <span className="ml-4 py-[2px] px-[10px] rounded-md bg-white text-[14px] font-semibold leading-6 text-light-blue cursor-auto "> + {report_count} + </span> + </div> + + {pathname === "/profile/personal" ? ( + <LogoutButton /> + ) : ( + <Link id="profile-nav__create-report" href="/create-report"> + Написать обращение + </Link> + )} + </nav> + ); +}; + +export default ProfileNav;