Add profile page
This commit is contained in:
parent
a106044047
commit
95674a489c
@ -1,8 +1,6 @@
|
|||||||
/* eslint-disable @next/next/no-img-element */
|
/* eslint-disable @next/next/no-img-element */
|
||||||
import { apiInstance } from "@/shared/config/apiConfig";
|
import { apiInstance } from "@/shared/config/apiConfig";
|
||||||
import { INews, INewsData, NewsDataResponse } from "@/shared/types/news-type";
|
import { INews, NewsDataResponse } from "@/shared/types/news-type";
|
||||||
import Image from "next/image";
|
|
||||||
import calendar from "./icons/calendar.svg";
|
|
||||||
import { Metadata } from "next";
|
import { Metadata } from "next";
|
||||||
import { Container, Title } from "@/shared/ui";
|
import { Container, Title } from "@/shared/ui";
|
||||||
|
|
||||||
|
45
src/app/[locale]/profile/layout.tsx
Normal file
45
src/app/[locale]/profile/layout.tsx
Normal file
@ -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 (
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<Title text="Личный кабинет" className="mb-[50px]" />
|
||||||
|
<ProfileNav report_count={data?.report_count as number} />
|
||||||
|
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Profile;
|
13
src/app/[locale]/profile/page.tsx
Normal file
13
src/app/[locale]/profile/page.tsx
Normal file
@ -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;
|
29
src/features/LogoutButton.tsx
Normal file
29
src/features/LogoutButton.tsx
Normal file
@ -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;
|
@ -1,6 +1,5 @@
|
|||||||
import { usePathname } from "next/navigation";
|
|
||||||
import { useSession } from "next-auth/react";
|
import { useSession } from "next-auth/react";
|
||||||
import { Link } from "@/shared/config/navigation";
|
import { Link, usePathname } from "@/shared/config/navigation";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
|
|
||||||
interface INavAuthProps {
|
interface INavAuthProps {
|
||||||
@ -8,29 +7,30 @@ interface INavAuthProps {
|
|||||||
setOpenMenu: (open: boolean) => void;
|
setOpenMenu: (open: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const NavAuth: React.FC<INavAuthProps> = ({
|
const NavAuth: React.FC<INavAuthProps> = ({ responsible, setOpenMenu }) => {
|
||||||
responsible,
|
|
||||||
setOpenMenu,
|
|
||||||
}: INavAuthProps) => {
|
|
||||||
const t = useTranslations("navigation");
|
const t = useTranslations("navigation");
|
||||||
const session = useSession();
|
const session = useSession();
|
||||||
const auth = session.status === "authenticated" ? true : false;
|
const auth = session.status === "authenticated";
|
||||||
|
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
const isActiveProfile = pathname === "/profile";
|
const isActiveProfile = pathname === "/profile";
|
||||||
const isActiveSignIn = pathname === "/sign-in";
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
{auth ? (
|
{auth ? (
|
||||||
<Link
|
<Link
|
||||||
onClick={() => setOpenMenu(false)}
|
onClick={() => setOpenMenu(false)}
|
||||||
href="/profile/personal"
|
href="/profile/personal"
|
||||||
className={`${
|
className={linkStyles(isActiveProfile)}
|
||||||
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]"
|
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
{t("profile")}
|
{t("profile")}
|
||||||
</Link>
|
</Link>
|
||||||
@ -38,15 +38,7 @@ const NavAuth: React.FC<INavAuthProps> = ({
|
|||||||
<Link
|
<Link
|
||||||
onClick={() => setOpenMenu(false)}
|
onClick={() => setOpenMenu(false)}
|
||||||
href="/sign-in"
|
href="/sign-in"
|
||||||
className={`${
|
className={linkStyles(isActiveSignIn)}
|
||||||
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"
|
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
{t("login")}
|
{t("login")}
|
||||||
</Link>
|
</Link>
|
||||||
|
@ -48,7 +48,7 @@ const Navbar = () => {
|
|||||||
{LINKS().map((link) => (
|
{LINKS().map((link) => (
|
||||||
<Link
|
<Link
|
||||||
className={`text-black opacity-0.5 size-4 font-normal min-w-[150px] flex justify-center${
|
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}
|
key={link.id}
|
||||||
href={link.pathname}
|
href={link.pathname}
|
||||||
|
47
src/widgets/ProfileNav/ProfileNav.tsx
Normal file
47
src/widgets/ProfileNav/ProfileNav.tsx
Normal file
@ -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;
|
Loading…
Reference in New Issue
Block a user