Add profile avatar

This commit is contained in:
ariari04 2024-09-11 20:24:35 +06:00
parent 95674a489c
commit 81aafdda7c
6 changed files with 331 additions and 0 deletions

View File

@ -0,0 +1,41 @@
"use server";
import ProfileAvatar from "@/features/ProfileAvatar/ProfileAvatar";
import { apiInstance } from "@/shared/config/apiConfig";
import { authConfig } from "@/shared/config/authConfig";
import { IProfile } from "@/shared/types/profile-type";
import { AxiosError } from "axios";
import { getServerSession } from "next-auth";
import React from "react";
const Personal = async () => {
const session = await getServerSession(authConfig);
const getProfile = async () => {
const Authorization = `Bearer ${session?.access_token}`;
const config = {
headers: {
Authorization,
},
};
try {
const response = await apiInstance.get<IProfile>(
"/users/profile/",
config
);
return response.data;
} catch (error: unknown) {
if (error instanceof AxiosError) console.log(error.message);
}
};
const data = await getProfile();
return (
<div className="personal">
<ProfileAvatar img={data?.image as string} />
</div>
);
};
export default Personal;

View File

@ -0,0 +1,244 @@
"use client";
import Image from "next/image";
import pen from "./icons/pen.svg";
import close from "./icons/close.svg";
import close_white from "./icons/close-white.svg";
import { authInstanse } from "@/shared/config/apiConfig";
import { useRouter } from "next/navigation";
import { useSession } from "next-auth/react";
import { useState } from "react";
import { AxiosError } from "axios";
import Loader from "@/shared/ui/Loader/Loader";
interface IProfileAvatarProps {
img: string;
}
const ProfileAvatar: React.FC<IProfileAvatarProps> = ({
img,
}: IProfileAvatarProps) => {
const [modal, setModal] = useState<boolean>(false);
const [display_image, setDisplayImage] = useState<File | string>(img);
const [isDeleting, setIsDeleting] = useState<boolean>(false);
const [message, setMessage] = useState<boolean>(false);
const [success, setSuccess] = useState<string>("");
const [loader, setLoader] = useState<boolean>(false);
const [error, setError] = useState<string>("");
const session = useSession();
const router = useRouter();
const def = "lflvl";
const handleChange: React.ChangeEventHandler<HTMLInputElement> = (e) => {
if (e.target.files) {
setDisplayImage(e.target.files[0]);
}
};
const changeImage = async () => {
const formData = new FormData();
if (session.status === "unauthenticated") return;
if (typeof display_image === typeof "string" || display_image === img)
return;
formData.append("image", display_image);
try {
setLoader(true);
const res = await authInstanse(
session.data?.access_token as string
).patch("/users/update_image/", formData);
setError("");
setSuccess("Фото профиля обновлено");
setMessage(true);
router.refresh();
setTimeout(() => {
setMessage(false);
}, 3000);
} catch (error: unknown) {
if (error instanceof AxiosError) {
setSuccess("");
setError(error.message);
setMessage(true);
setTimeout(() => {
setMessage(false);
}, 3000);
}
} finally {
setLoader(false);
}
};
const returnDefaultImage = async () => {
if (session.status === "unauthenticated") return;
try {
setLoader(true);
const res = await authInstanse(
session.data?.access_token as string
).patch("/users/delete_image/", {});
setError("");
setSuccess("Фото профиля удалено");
setMessage(true);
router.refresh();
setTimeout(() => {
setMessage(false);
}, 3000);
} catch (error: unknown) {
if (error instanceof AxiosError) {
setSuccess("");
setError(error.message);
setMessage(true);
setTimeout(() => {
setMessage(false);
}, 3000);
}
} finally {
setLoader(false);
}
};
const imageIsString =
typeof display_image === "string"
? display_image
: typeof display_image === "undefined"
? ""
: URL.createObjectURL(display_image as File);
return (
<div className="mb-[25px] relative w-[135px] h-[135px]">
<img
className="w-full h-full rounded-lg object-cover"
src={img}
alt="User Image"
/>
<button
onClick={() => setModal(true)}
className="absolute bottom-0 right-0 w-[50px] h-[50px] flex items-center justify-center shadow-sm bg-white rounded-lg cursor-pointer"
>
<Image src={pen} alt="Pen Icon" width={22} height={22} />
</button>
{modal && (
<div
onClick={() => setModal(false)}
className="w-full h-full fixed top-0 left-0 flex items-center justify-center bg-black"
>
<div
onClick={(e) => e.stopPropagation()}
className="relative max-w-[400px] p-6 flex flex-col items-center bg-white rounded-sm shadow-sm"
>
<div>
<div className="flex items-center justify-between">
<h4 className="text-[14px] font-semibold leading-6 ">
Фото профиля
</h4>
<button
className="w-6 h-6 flex items-center justify-center border border-white rounded-lg"
onClick={() => setModal(false)}
>
<Image src={close} alt="Close Icon" />
</button>
</div>
<p className="w-[90%] text-[14px] leading-5 text-gray-400">
По фото профиля другие люди смогут вас узнавать, а вам будет
проще определять, в какой аккаунт вы вошли.
</p>
</div>
<img
className="my-6 w-[130px] h-[130px] rounded-lg object-cover"
src={imageIsString}
alt="User image"
/>
<div className="flex items-center gap-2">
{img === def && display_image === def ? (
<>
<label
className="min-w-[110px] py-2 px-4 mt-6 flex items-center justify-center text-[14px] font-semibold leading-5 rounded-sm cursor-pointer"
htmlFor="change-image"
>
Добавить фото профиля
</label>
<input
onChange={handleChange}
id="change-image"
type="file"
/>
</>
) : isDeleting ? (
<>
<button
onClick={() => setIsDeleting(false)}
className="bg-white text-black"
>
Отмена
</button>
<button
onClick={returnDefaultImage}
disabled={loader}
className="bg-blue text-white"
>
{loader ? <Loader /> : "Удалить"}
</button>
</>
) : img === display_image || success !== "" ? (
<>
<button
onClick={() => setIsDeleting(true)}
className="bg-white text-black"
>
Удалить
</button>
<label className="bg-blue text-white" htmlFor="change-image">
Сменить
</label>
<input
onChange={handleChange}
id="change-image"
type="file"
/>
</>
) : (
<>
<button
onClick={() => setDisplayImage(img)}
className="bg-white text-black"
>
Назад
</button>
<button
disabled={loader}
onClick={changeImage}
className="bg-blue text-white"
>
{loader ? <Loader /> : "Сохранить"}
</button>
</>
)}
</div>
{message && (
<div className="absolute bottom-[32px] p-2 w-[302px] flex items-center justify-between rounded-sm bg-black text-[14px] font-semibold leading-5 text-white">
<p>
{success} {error}
</p>
<button className="text-0" onClick={() => setMessage(false)}>
<Image src={close_white} alt="Close Icon White" />
</button>
</div>
)}
</div>
</div>
)}
</div>
);
};
export default ProfileAvatar;

View File

@ -0,0 +1,14 @@
<svg width="20.000000" height="20.000000" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<desc>
Created with Pixso.
</desc>
<defs>
<clipPath id="clip2704_56355">
<rect id="icon" width="20.000000" height="20.000000" fill="white" fill-opacity="0"/>
</clipPath>
</defs>
<g clip-path="url(#clip2704_56355)">
<path id="Vector" d="M14.375 5.625L5.625 14.375" stroke="#FFFFFF" stroke-opacity="1.000000" stroke-width="1.500000" stroke-linejoin="round" stroke-linecap="round"/>
<path id="Vector" d="M5.625 5.625L14.375 14.375" stroke="#FFFFFF" stroke-opacity="1.000000" stroke-width="1.500000" stroke-linejoin="round" stroke-linecap="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 730 B

View File

@ -0,0 +1,14 @@
<svg width="12.000000" height="12.000000" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<desc>
Created with Pixso.
</desc>
<defs>
<clipPath id="clip4_25648">
<rect id="close" width="12.000000" height="12.000000" fill="white" fill-opacity="0"/>
</clipPath>
</defs>
<g clip-path="url(#clip4_25648)">
<path id="Vector" d="M8.625 3.375L3.375 8.625" stroke="#334155" stroke-opacity="1.000000" stroke-width="1.500000" stroke-linejoin="round" stroke-linecap="round"/>
<path id="Vector" d="M3.375 3.375L8.625 8.625" stroke="#334155" stroke-opacity="1.000000" stroke-width="1.500000" stroke-linejoin="round" stroke-linecap="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 721 B

View File

@ -0,0 +1,8 @@
<svg width="21.908203" height="22.000000" viewBox="0 0 21.9082 22" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<desc>
Created with Pixso.
</desc>
<defs/>
<path id="Vector (Stroke)" d="M14.7539 1.22754C16.3906 -0.40918 19.041 -0.40918 20.6777 1.22754C22.3145 2.86426 22.3145 5.51562 20.6777 7.15234L8.5918 19.2383C8.45508 19.375 8.28516 19.4707 8.09766 19.5166L4.51562 20.3867C4.47852 20.3984 4.44141 20.4072 4.4043 20.4141L2.23047 20.9424C1.875 21.0293 1.5 20.9238 1.24023 20.665C0.982422 20.4062 0.876953 20.0312 0.962891 19.6758L2.38867 13.8076C2.43555 13.6211 2.53125 13.4502 2.66797 13.3145L14.7539 1.22754ZM4.53711 18.2236L7.31641 17.5479L19.1953 5.66895C20.0117 4.85156 20.0117 3.52832 19.1953 2.71094C18.377 1.89355 17.0547 1.89355 16.2363 2.71094L4.35742 14.5889L3.68359 17.3652L4.53711 18.2236Z" fill="#B2B5BE" fill-opacity="1.000000" fill-rule="evenodd"/>
<path id="Union" d="M14.5332 4.09473C14.9434 3.68652 15.6074 3.6875 16.0176 4.09766L19.123 7.21191C19.5312 7.62207 19.5312 8.28613 19.1211 8.69531C18.7109 9.10449 18.0469 9.10352 17.6367 8.69336L14.5312 5.57812C14.123 5.16797 14.123 4.50391 14.5332 4.09473ZM8.77734 19.9238C8.77734 19.3447 9.24609 18.875 9.82617 18.875L20.8594 18.875C21.4375 18.875 21.9082 19.3447 21.9082 19.9238C21.9082 20.5029 21.4375 20.9727 20.8594 20.9727L9.82617 20.9727C9.24609 20.9727 8.77734 20.5029 8.77734 19.9238Z" fill="#B2B5BE" fill-opacity="1.000000" fill-rule="evenodd"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,10 @@
export interface IProfile {
id: number;
email: string;
first_name: string;
last_name: string;
image: string;
role: number;
govern_status: null;
report_count: number;
}