Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
278 changes: 194 additions & 84 deletions src/app/(dashboard)/profile/components/EditProfileComponent.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,37 @@
"use client";
import React, { useState } from 'react';
import React, { useEffect, useState } from 'react';
import { useRouter } from 'next/navigation';
import { GetProfileService } from '@/services/profile-services';
import { MemberProfileDetails } from '@/types/types';
import toast from 'react-hot-toast';

type ProfileData = {
name: string;
email: string;
username: string;
bio: string;
profileImage: string;
}

type EditProfileProps = {
onCancel: () => void;
}

export default function EditProfileComponent({ onCancel }: EditProfileProps) {
const router = useRouter();

const [profileData, setProfileData] = useState<ProfileData>({
name: "John Doe",
email: "[email protected]",
username: "johndoe",
bio: "just a random code to have a profile structure to later code on",
profileImage: "/placeholder.webp"

const [profileData, setProfileData] = useState<MemberProfileDetails>({
memberId:0,
name: "",
rollNo: "",
sex: "",
track: "",
email: "",
hostel: '',
discordId: '',
macAddress: '',
});

const tracks = ['Web', 'Systems', 'AI', 'Mobile'];


const [isSubmitting, setIsSubmitting] = useState(false);
const [previewUrl, setPreviewUrl] = useState<string>(profileData.profileImage);
const [isLoading, setIsLoading] = useState(true);
const [GenToggle, setGenToggle] = useState([false, false]);
const [previewUrl, setPreviewUrl] = useState<string>("/placeholder.webp");
const [isUserEnrolling, setUserEnrolling] = useState(true);

const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
const { name, value } = e.target;
Expand All @@ -36,12 +41,25 @@ export default function EditProfileComponent({ onCancel }: EditProfileProps) {
});
};

useEffect(()=>{
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do u want to fetch the member details again? Can't you use the safe data that was fetched for the view? It reduces a query to the backend, plus useEffect like this are not really standard in NextJS, its a good practice to fetch the data inside a server component (refer this)

async function getProfileDetails() {
const member = await GetProfileService.getProfileDetails();
setGenToggle([member?.sex == "M",member?.sex == "F"]);
if(member){
setProfileData(member);
setIsLoading(false);
}
else console.log("Error Fetching User Data!");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use toast.error instead of console unless for debugging purposes

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The page will just keep on loading if there is no member data. handle this as enrolment for now, just let them enter their details

}
getProfileDetails();
},[])

const handleImageChange = (e: React.ChangeEvent<HTMLInputElement>) => {
if (e.target.files && e.target.files[0]) {
const file = e.target.files[0];
const fileUrl = URL.createObjectURL(file);
setPreviewUrl(fileUrl);

// You would typically upload the image to your server here
// and then update the profileData with the returned URL
// For this demo, we'll just update the preview
Expand All @@ -51,13 +69,18 @@ export default function EditProfileComponent({ onCancel }: EditProfileProps) {
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setIsSubmitting(true);

// Simulate API call
setTimeout(() => {
async function Update() {
let data = await GetProfileService.UpdateProfileDetails(profileData);
if(data){
setProfileData(data);
handleCancel();
}
else{
console.log("Error in Updating User");
}
setIsSubmitting(false);
// Always redirect back to the main profile page after successful update
router.push('/profile');
}, 1000);
}
Update();
};

const handleCancel = () => {
Expand All @@ -69,103 +92,190 @@ export default function EditProfileComponent({ onCancel }: EditProfileProps) {
}
};

return (
const trackUi: JSX.Element[] = tracks.map((track) => (
<option key={track} value={track}>
{track}
</option>
));

if(!isLoading){
return (
<div className="container mx-auto px-4 py-8">
<div className="bg-panelColor rounded-lg shadow-md p-6">
<h1 className="text-2xl font-bold text-white mb-6">Edit Profile</h1>

<form onSubmit={handleSubmit}>
{isUserEnrolling ? (
<h1 className="text-2xl font-bold text-white mb-2">SetUp Profile</h1>
) : (
<h1 className="text-2xl font-bold text-white mb-2">Update Profile</h1>
)}
<hr />
<form onSubmit={handleSubmit}>
<div className="bg-panelColor rounded-lg mt-6 shadow-md p-6">
<h1 className="text-2xl font-bold text-white mb-6">Personal Details</h1>
<div className="flex flex-col md:flex-row gap-6 mb-6">
<div className="flex-shrink-0">
<div className="relative">
<img
src={previewUrl}
alt={profileData.name}
className={`w-32 h-32 rounded-full object-cover border-2 border-primaryYellow`}
/>
<label
htmlFor="profileImage"
className={`absolute bottom-0 right-0 bg-primaryYellow p-2 rounded-full cursor-pointer`}
>
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm supposing this is a placeholder image, u can actually use an icon from Lucid React for this

<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z" />
</svg>
<input
id="profileImage"
type="file"
accept="image/*"
onChange={handleImageChange}
className="hidden"
/>
</label>
</div>
</div>

<div className="flex-grow">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
<div>
<label className="block text-gray-400 text-sm mb-1">Name</label>
<input
type="text"
name="name"
disabled={!isUserEnrolling}
value={profileData.name}
onChange={handleChange}
className={`w-full px-3 py-2 bg-bgMainColor text-white border border-gray-700 rounded-md focus:outline-none focus:border-primaryYellow ${!isUserEnrolling ? 'opacity-50 cursor-not-allowed' : ''}`}
/>
</div>
<div>
<label className="block text-gray-400 text-sm mb-1">Gender</label>
<div className="grid grid-cols-2 gap-3 mt-2 w-full">
{['Male', 'Female'].map((label, idx) => (
<button
key={label}
type="button"
disabled={!isUserEnrolling}
className={`w-full px-4 py-2 h-10 rounded-full transition-colors border border-gray-700 font-semibold truncate
${GenToggle[idx] ? 'bg-primaryYellow text-black shadow-lg' : 'bg-bgMainColor text-white hover:bg-gray-800'}
${!isUserEnrolling ? 'opacity-50 cursor-not-allowed' : ''}
`}
onClick={() => {
setGenToggle([idx === 0, idx === 1]);
setProfileData({ ...profileData, sex: label == 'Male' ? 'M' : 'F' });
}}
>
{label}
</button>
))}
</div>
</div>
<div>
<label className="block text-gray-400 text-sm mb-1">Track</label>
<select
name="track"
value={profileData.track}
onChange={(e) => setProfileData({ ...profileData, track: e.target.value })}
disabled={!isUserEnrolling}
className={`w-full px-3 py-2 bg-bgMainColor text-white border border-gray-700 rounded-md focus:outline-none focus:border-primaryYellow ${!isUserEnrolling ? 'opacity-50 cursor-not-allowed' : ''}`}
>
{trackUi}
</select>
</div>
<div>
<label className="block text-gray-400 text-sm mb-1">Roll Number</label>
<input
type="text"
disabled={!isUserEnrolling}
name="rollNo"
value={profileData.rollNo}
onChange={handleChange}
className={`w-full px-3 py-2 bg-bgMainColor text-white border border-gray-700 rounded-md focus:outline-none focus:border-primaryYellow ${!isUserEnrolling ? 'opacity-50 cursor-not-allowed' : ''}`}
/>
</div>
</div>
</div>
</div>
</div>

<div className="bg-panelColor rounded-lg lg:mt-16 sm:mt-8 shadow-md p-6">
<h1 className="text-2xl font-bold text-white mb-6">Additional Details</h1>
<div className="flex flex-col md:flex-row gap-6 mb-6">
<div className="flex-shrink-0">
<div className="relative">
<img
src={previewUrl}
alt={profileData.name}
className="w-32 h-32 rounded-full object-cover border-2 border-primaryYellow"
/>
<label
htmlFor="profileImage"
className="absolute bottom-0 right-0 bg-primaryYellow p-2 rounded-full cursor-pointer"
>
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z" />
</svg>
<input
id="profileImage"
type="file"
accept="image/*"
onChange={handleImageChange}
className="hidden"
/>
</label>
</div>
</div>

<div className="flex-grow">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
<div>
<label className="block text-gray-400 text-sm mb-1">Name</label>
<input
<label className="block text-gray-400 text-sm mb-1">Hostel</label>
<input
type="text"
name="name"
value={profileData.name}
name="hostel"
value={profileData.hostel}
onChange={handleChange}
className="w-full px-3 py-2 bg-bgMainColor text-white border border-gray-700 rounded-md focus:outline-none focus:border-primaryYellow"
/>
</div>

<div>
<label className="block text-gray-400 text-sm mb-1">Email</label>
<input
type="email"
name="email"
value={profileData.email}
onChange={handleChange}
className="w-full px-3 py-2 bg-bgMainColor text-white border border-gray-700 rounded-md focus:outline-none focus:border-primaryYellow"
/>
</div>
<div>
<label className="block text-gray-400 text-sm mb-1">Username</label>
<input
<label className="block text-gray-400 text-sm mb-1">Discord Id</label>
<input
type="text"
name="username"
value={profileData.username}
name="discordId"
value={profileData.discordId}
onChange={handleChange}
className="w-full px-3 py-2 bg-bgMainColor text-white border border-gray-700 rounded-md focus:outline-none focus:border-primaryYellow"
/>
</div>
<div>
<label className="block text-gray-400 text-sm mb-1">Email</label>
<input
type="email"
name="email"
value={profileData.email}
<label className="block text-gray-400 text-sm mb-1">Mac Address</label>
<input
type="text"
name="macAddress"
value={profileData.macAddress}
onChange={handleChange}
className="w-full px-3 py-2 bg-bgMainColor text-white border border-gray-700 rounded-md focus:outline-none focus:border-primaryYellow"
/>
</div>
</div>

<div>
<label className="block text-gray-400 text-sm mb-1">Bio</label>
<textarea
name="bio"
value={profileData.bio}
onChange={handleChange}
rows={4}
className="w-full px-3 py-2 bg-bgMainColor text-white border border-gray-700 rounded-md focus:outline-none focus:border-primaryYellow"
/>
</div>
</div>
</div>

<div className="flex justify-end space-x-4">
<button
<button
onClick={handleCancel}
type="button"
className="px-4 py-2 border border-gray-600 text-white rounded-md hover:bg-gray-800 transition-colors"
>
Cancel
</button>
<button
type="submit"
<button
type="submit"
className="px-4 py-2 bg-primaryYellow text-black rounded-md hover:bg-opacity-90 transition-colors"
disabled={isSubmitting}
>
{isSubmitting ? "Saving..." : "Save Changes"}
</button>
</div>
</form>
</div>
</div>
</form>

</div>
);
}else{
return(
<div className="flex items-center justify-center w-full min-h-screen">
<h1 className="text-white text-lg font-medium">Loading...</h1>
</div>
)
}

}
Loading