PROFILE PICTURES 🔥
This commit is contained in:
BIN
client/assets/NoProfilePicture.jpeg
Normal file
BIN
client/assets/NoProfilePicture.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.5 KiB |
@@ -16,8 +16,8 @@ import {
|
||||
} from "@nextui-org/react";
|
||||
import { Form, Formik } from "formik";
|
||||
import NextUIFormikInput from "./NextUIFormikInput";
|
||||
import { PencilSquareIcon } from "../icons";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import UserProfilePicture from "./UserProfilePicture";
|
||||
|
||||
export default function UpdateAccountModule({
|
||||
accessToken,
|
||||
@@ -157,7 +157,7 @@ export default function UpdateAccountModule({
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-row gap-8">
|
||||
<div className="flex-grow flex sm:flex-row flex-col gap-4 *:w-full *:flex *:flex-col *:gap-4">
|
||||
<div className="flex-grow flex sm:flex-row flex-col gap-4 *:w-full *:flex *:flex-col *:gap-4 *:my-auto">
|
||||
<div>
|
||||
<NextUIFormikInput
|
||||
label="First Name"
|
||||
@@ -196,12 +196,12 @@ export default function UpdateAccountModule({
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-40 h-40 bg-red-500 hover:bg-red-700 transition-colors rounded-full relative">
|
||||
<div className="transition-opacity opacity-0 hover:opacity-100 absolute w-full h-full text-white flex flex-col justify-center rounded-full">
|
||||
<div className=" w-min h-min mx-auto scale-150">
|
||||
<PencilSquareIcon />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<UserProfilePicture
|
||||
userId={userInformation.id}
|
||||
token={accessToken}
|
||||
editable={true}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
|
||||
93
client/src/components/UserProfilePicture.tsx
Normal file
93
client/src/components/UserProfilePicture.tsx
Normal file
@@ -0,0 +1,93 @@
|
||||
import axios from "axios";
|
||||
import React, { useRef, useState } from "react";
|
||||
import config from "../config";
|
||||
import { Button, Image } from "@nextui-org/react";
|
||||
|
||||
export default function UserProfilePicture({
|
||||
userId,
|
||||
token,
|
||||
editable,
|
||||
}: {
|
||||
userId: string;
|
||||
token: string;
|
||||
editable: boolean;
|
||||
}) {
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const uploadProfileImage = async (
|
||||
userId: string,
|
||||
file: File,
|
||||
token: string
|
||||
) => {
|
||||
const formData = new FormData();
|
||||
formData.append("image", file);
|
||||
|
||||
try {
|
||||
const response = await axios.put(
|
||||
`${config.serverAddress}/users/profile-image/${userId}`,
|
||||
formData,
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "multipart/form-data",
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
}
|
||||
);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const handleButtonClick = () => {
|
||||
if (fileInputRef.current) {
|
||||
fileInputRef.current.click();
|
||||
}
|
||||
};
|
||||
|
||||
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (event.target.files) {
|
||||
const file = event.target.files[0];
|
||||
uploadAndHandleSubmit(file);
|
||||
}
|
||||
};
|
||||
|
||||
const uploadAndHandleSubmit = async (file: File) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
await uploadProfileImage(userId, file, token);
|
||||
} catch (error) {
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<input
|
||||
aria-label="profile image selector"
|
||||
ref={fileInputRef}
|
||||
className="hidden"
|
||||
type="file"
|
||||
accept="image/*"
|
||||
onChange={handleFileChange}
|
||||
/>
|
||||
<Button
|
||||
className="w-48 h-48 p-0 border-2 border-red-500 shadow-red-500 shadow-2xl"
|
||||
radius="full"
|
||||
onPress={editable ? handleButtonClick : () => {}}
|
||||
isLoading={loading}
|
||||
>
|
||||
{loading ? (
|
||||
<></>
|
||||
) : (
|
||||
<Image
|
||||
src={`${config.serverAddress}/users/profile-image/${userId}`}
|
||||
radius="full"
|
||||
/>
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import { LockClosedIcon, PencilSquareIcon } from "../icons";
|
||||
import SpringboardButton from "../components/SpringboardButton";
|
||||
import { getTimeOfDay } from "../utilities";
|
||||
import { retrieveUserInformation } from "../security/users";
|
||||
import UserProfilePicture from "../components/UserProfilePicture";
|
||||
|
||||
export default function SpringboardPage() {
|
||||
let { accessToken } = useParams<string>(); // TODO: Replace AT from props with AT from localstorage
|
||||
@@ -65,7 +66,11 @@ export default function SpringboardPage() {
|
||||
Manage your account
|
||||
</Button>
|
||||
</div>
|
||||
<div className="bg-red-500 w-40 h-40 rounded-full"></div>
|
||||
<UserProfilePicture
|
||||
userId={userInformation.id}
|
||||
token={accessToken!}
|
||||
editable={false}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-row justify-stretch *:w-full *:h-56 w-full p-4 pt-0 gap-4">
|
||||
<SpringboardButton
|
||||
|
||||
Reference in New Issue
Block a user