Skip to content

Commit

Permalink
Merge pull request #79 from ChangePlusPlusVandy/profile-pic-upload
Browse files Browse the repository at this point in the history
Profile pic upload
  • Loading branch information
JiashuHarryHuang authored Feb 11, 2024
2 parents c506aa2 + 4282a3d commit fc1dbc1
Show file tree
Hide file tree
Showing 18 changed files with 1,380 additions and 638 deletions.
35 changes: 25 additions & 10 deletions components/DesktopSidebar/UserIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,38 @@ import {
} from '@/styles/components/Sidebar/userIcon.styles';
import { Media } from '@/lib/media';
import { useSession } from 'next-auth/react';
import { useUserData } from '@/lib/useUserData';
require('dotenv').config(); // Load environment variables from .env file

export const UserIcon = () => {
const { data: session } = useSession();
const userData = useUserData();

return (
<UserIconContainer>
<ImageContainer>
{/* TODO: add user profile image */}
{/* Bigger image size for desktop and smaller for mobile */}
<Media greaterThanOrEqual="sm">
<Image src="/bookem-logo.png" width="100" height="100" alt="" />
</Media>
<Media lessThan="sm">
<Image src="/bookem-logo.png" width="73" height="73" alt="" />
</Media>
{userData && (
<>
{/* Choose image size based on screen size */}
<Media greaterThanOrEqual="sm">
<Image
src={userData.profileImgUrl || '/bookem-logo.png'}
width="100"
height="100"
alt="user-profile"
/>
</Media>
<Media lessThan="sm">
<Image
src={userData.profileImgUrl || '/bookem-logo.png'}
width="73"
height="73"
alt="user-profile"
/>
</Media>
</>
)}
</ImageContainer>
<Name>{session?.user && session.user.name}</Name>
<Name>{userData?.name}</Name>
</UserIconContainer>
);
};
4 changes: 0 additions & 4 deletions components/Home/MainDashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -133,13 +133,9 @@ const MainDashboard = ({ userData }: { userData: QueriedUserData | null }) => {
</Media>

{/* Desktop PastActivity is not located at bottom of main dashboard */}
<Media greaterThanOrEqual="sm">
{/* Desktop PastActivity is not located here */}
</Media>
</Container>

{/* Mobile PastActivity is hidden */}
<Media lessThan="sm">{/**PastActivity is not shown here */}</Media>

{/* Desktop PastActivity is shown on the right side of main dashboard*/}
<Media greaterThanOrEqual="sm">
Expand Down
140 changes: 72 additions & 68 deletions components/Register/LastRegisterPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { FieldValues } from 'react-hook-form';
import { signIn } from 'next-auth/react';
import { RegisterFormData } from '@/utils/types';
import Image from 'next/image';
import { message } from 'antd';
import {
LastPageContainer,
LastPageTextContainer,
Expand All @@ -14,74 +15,8 @@ import {
UploadPictureContainer,
} from '@/styles/register.styles';

/* TODO: MOVE ALL OF THIS TO BACKEND API */
import S3 from 'aws-sdk/clients/s3';
import axios from 'axios';

const s3 = new S3({
region: 'us-east-2',
accessKeyId: process.env.NEXT_PUBLIC_ACCESS_KEY,
secretAccessKey: process.env.NEXT_PUBLIC_SECRET_KEY,
signatureVersion: 'v4',
});

// upload file to S3 Bucket
const uploadS3 = async (file: File, email: string) => {
try {
// put file in S3 bucket
const fileParams = {
Bucket: process.env.NEXT_PUBLIC_BUCKET_NAME,
Key: file.name,
Expires: 600,
ContentType: file.type,
};

const putURL = await s3.getSignedUrlPromise('putObject', fileParams);

await axios.put(putURL, file, {
headers: {
'Content-type': String(file.type),
},
});

// get file's presigned URL from S3 bucket
const getURL = await s3.getSignedUrlPromise('getObject', {
Bucket: process.env.NEXT_PUBLIC_BUCKET_NAME,
Key: file.name,
});

const imageData = await Promise.resolve(fetch(getURL));

console.log(imageData.url, typeof imageData.url);

// send PATCH request to /api/users/upload-profile to update user's profile picture
const res = await axios.patch(
'/api/users/upload-profile',
{
profileImgUrl: imageData.url,
},
{
headers: {
'Content-Type': 'application/json',
},
}
);

if (res.status !== 200) {
alert(
'Error uploading profile picture. Please try again or contact us if the problem persists.'
);
} else {
alert('Profile picture uploaded successfully!');
}

// Return the status of the user update
return { message: 'User updated with picture', error: null };
} catch (e) {
return { message: 'An error occurred', error: e };
}
};

const LastRegisterPage = ({ formData }: { formData: RegisterFormData }) => {
// state for uploaded picture file
const [pictureFile, setPictureFile] = useState<File | undefined>();
Expand All @@ -92,18 +27,83 @@ const LastRegisterPage = ({ formData }: { formData: RegisterFormData }) => {
// object that helps with handling clicking on picture upload button
const inputRef = useRef<HTMLInputElement | null>(null);

const [messageApi, contextHolder] = message.useMessage();

// handles clicking on picture upload button
const handleUploadClick = () => {
inputRef.current?.click();
};

const uploadS3 = async (file: File, email: String) => {
const formData = new FormData();
formData.append('file', file);

// formData.append('email', email);
try {
const res = await fetch('/api/users/upload-s3', {
method: 'POST',
body: formData,
});

if (!res.ok) throw new Error(`Error: ${res.status}`);
const imageUrl = await res.json();
console.log(imageUrl, typeof imageUrl);
uploadDB(imageUrl.fileName);
return { message: 'User updated with picture', error: null };
} catch (err) {
return { message: 'An error occurred', error: err };
}
};

const uploadDB = async (fileName: String) => {
try {
console.log(fileName);
const res = await axios.patch(
'/api/users/upload-profile',
{
// profileImgUrl: imageData.url,
profileImgUrl: fileName,
},
{
headers: {
'Content-Type': 'application/json',
},
}
);

if (res.status !== 200) {
messageApi.open({
type: 'error',
content: 'Sorry, an error occurred',
});
} else {
messageApi.open({
type: 'success',
content: 'Profile image uploaded successfully',
});
}
// Return the status of the user update
return { message: 'User updated with picture', error: null };
} catch (e) {
return { message: 'An error occurred', error: e };
}
};

// updates name of picture upload button to the name of the file uploaded
const handleFileChange = (e: ChangeEvent<HTMLInputElement>) => {
if (!e.target.files || e.target.files[0] == undefined) {
return;
}
setPictureFile(e.target.files[0]);
setPictureURL(URL.createObjectURL(e.target.files[0]));
//check if file uploaded is the right type
const validImageTypes = ['image/jpeg', 'image/png', 'image/jpg'];
const file = e.target.files[0];
if (validImageTypes.includes(file.type)) {
setPictureFile(file);
setPictureURL(URL.createObjectURL(file));
} else {
alert('Please upload a valid image file (JPEG, PNG, JPG)');
//TODO: test with non-jpeg file
}
};

// uploads picture file to S3 bucket
Expand All @@ -113,6 +113,8 @@ const LastRegisterPage = ({ formData }: { formData: RegisterFormData }) => {

if (res.error) console.log('Success!');
else console.log('error:', res.error);
} else {
alert('Please upload a picture'); //TODO: maybe make this look better
}
};

Expand All @@ -136,6 +138,8 @@ const LastRegisterPage = ({ formData }: { formData: RegisterFormData }) => {

return (
<LastPageContainer>
{/* Context holder for message API */}
{contextHolder}
<LastPageTextContainer>
<LastPageText>Thank you!</LastPageText>
<LastPageText>
Expand Down
2 changes: 2 additions & 0 deletions components/Register/RegisterPage1.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
InputFlex,
InputText,
InputContainer,
LoginLink,
} from '@/styles/register.styles';
import { dateIsValid, formatBirthday, formatPhoneNumber } from '@/utils/utils';

Expand Down Expand Up @@ -181,6 +182,7 @@ const RegisterPage1 = ({
{errors.state && printError('State is required')}
{errors.zip && printError('Zip code is required')}
</SectionContainer>
<LoginLink href="/login">Already have an account? Login here</LoginLink>
</Form>

<RegisterFlow
Expand Down
1 change: 1 addition & 0 deletions components/Register/RegisterPage2.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
CheckboxContainer,
InputText,
Fieldset,
LoginLink,
} from '@/styles/register.styles';

/**
Expand Down
1 change: 1 addition & 0 deletions components/Register/RegisterPage3.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
InputTextarea,
JoinNewsletterContainer,
Fieldset,
LoginLink,
} from '@/styles/register.styles';

const RegisterPage3 = ({
Expand Down
1 change: 1 addition & 0 deletions components/Register/RegisterPage4.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
LabelRadio,
InputRadio,
OtherRadio,
LoginLink,
} from '@/styles/register.styles';

const RegisterPage4 = ({
Expand Down
10 changes: 1 addition & 9 deletions components/Register/RegisterPage5.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
Button,
Fieldset,
JoinNewsletterContainer,
LoginLink,
} from '@/styles/register.styles';

const RegisterPage5 = ({
Expand Down Expand Up @@ -183,11 +184,6 @@ const RegisterPage5 = ({
</SectionContainer>
</Media>

{/* Desktop */}
<Media greaterThanOrEqual="sm">
{/** Moved to different location */}
</Media>

<SectionContainer>
<SectionHeader>
Why do you want to become a Book&apos;em volunteer?
Expand Down Expand Up @@ -257,9 +253,6 @@ const RegisterPage5 = ({
</JoinNewsletterContainer>
</SectionContainer>

{/* Mobile */}
<Media lessThan="sm">{/** Moved to different location */}</Media>

{/* Desktop */}
<Media greaterThanOrEqual="sm">
<SectionContainer>
Expand Down Expand Up @@ -352,7 +345,6 @@ const RegisterPage5 = ({
</SectionContainer>
</Form>
</FormBorder>

<ButtonContainer>
<Button form="registerPage5">Submit</Button>
</ButtonContainer>
Expand Down
12 changes: 12 additions & 0 deletions lib/s3config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { S3Client } from '@aws-sdk/client-s3';

export const buildS3 = () => {
return new S3Client({
region: process.env.AWS_REGION as string,
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID as string,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY as string,
// sessionToken: process.env.aws_session_token as string,
},
});
};
4 changes: 4 additions & 0 deletions next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ const nextConfig = {
// Reference a variable that was defined in the .env file and make it available at Build Time
MONGODB_URI: process.env.MONGODB_URI,
},
images: {
// include any domains used for images in the project
domains: ['bookem-user-profile.s3.us-east-2.amazonaws.com'],
},
trailingSlash: true,
};

Expand Down
Loading

0 comments on commit fc1dbc1

Please sign in to comment.