Skip to content

Commit

Permalink
adding user update functionality
Browse files Browse the repository at this point in the history
Refining UI and adding image

fixes #168 auth and admin login (#169)

adding way to delete image and split codes into separate components

changing icons and adjust way to update user

removing unused file

refactoring my codes to solve some issues

splitting some components into smaller ones

removing some lines not being used

adjusting button to remove image

adjusting some functionality and UI

refactoring code to reduce some issue

split some components into smaller one

fixing issues related to routes

making some changes to UI

rebasing changes from develop

stoping yarn to be tracked
  • Loading branch information
Ismaelmurekezi committed Oct 9, 2024
1 parent 9fcad26 commit f8c3d66
Show file tree
Hide file tree
Showing 18 changed files with 677 additions and 20,828 deletions.
Binary file added public/cover.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/components/profileDropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ function ProfileDropdown({
<div className="flex flex-col w-full gap-[5px] cursor-pointer">
<Link
onClick={handleShowProfileDropdown}
to="/admin/profile"
to="update-profile"
className="font-semibold text-black-600 dark:text-black px-4 py-2 hover:bg-gray-600 hover:text-gray-200 dark:hover:bg-gray-300 dark:hover:text-gray-900"
>
<>{t("Profile")}</>
Expand Down
26 changes: 26 additions & 0 deletions src/components/updateUserProfile/ImageUpload.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React from "react";
import { UseFormReturn } from "react-hook-form";
import { ThreeDots } from "react-loader-spinner";
import { TuserSchema } from "../../utils/userSchema";
import useImageUpload from "../../components/updateUserProfile/useImagePreview";
import ImagePreview from "../../components/updateUserProfile/imagePreview";

interface ImageUploadProps {
form: UseFormReturn<TuserSchema>;
}

const ImageUpload: React.FC<ImageUploadProps> = ({ form }) => {
const { previewImage, isUploading } = useImageUpload(form);

return (

<div className="w-full absolute top-20 px-8">
<span>
{isUploading && <ThreeDots height="30" width="30" color="#ffffff" />}
</span>
<ImagePreview previewImage={previewImage} form={form} />
</div>
);
};

export default ImageUpload;
57 changes: 57 additions & 0 deletions src/components/updateUserProfile/PasswordInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import React, { useState } from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faEye, faEyeSlash } from "@fortawesome/free-regular-svg-icons";

interface PasswordInputProps {
register: any;
errors: any;
}

const PasswordInput: React.FC<PasswordInputProps> = ({ register, errors }) => {
const [showPassword, setShowPassword] = useState(false);

const handleClickShowPassword = () => {
setShowPassword((prev) => !prev);
};

return (
<>
<div className="relative flex-1 rounded">
<div className="flex flex-1 flex-col gap-2">
<label htmlFor="" className="text-white">
Password
</label>
<input
type={showPassword ? "text" : "password"}
{...register("password")}
placeholder="Change password"
className="w-full pl-4 py-2 rounded-md border-[1px] bg-transparent dark:text-white"
/>
</div>

<div
onClick={handleClickShowPassword}
className="absolute right-4 top-10"
aria-label="Toggle password visibility"
>
{showPassword ? (
<FontAwesomeIcon
icon={faEye}
className="text-gray-400 dark:text-white"
/>
) : (
<FontAwesomeIcon
icon={faEyeSlash}
className="text-gray-400 dark:text-white"
/>
)}
</div>
</div>
{errors.password && (
<p className="text-sm text-red-600">{errors.password.message}</p>
)}
</>
);
};

export default PasswordInput;
130 changes: 130 additions & 0 deletions src/components/updateUserProfile/ProfileForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import React from "react";
import { UseFormReturn } from "react-hook-form";
import { Link } from "react-router-dom";
import { ThreeDots } from "react-loader-spinner";
import { TuserSchema } from "../../utils/userSchema";
import PasswordInput from "./PasswordInput";
import useImageUpload from "./useImagePreview";

interface ProfileFormProps {
form: UseFormReturn<TuserSchema>;
onSubmit: (data: TuserSchema) => Promise<void>;
}

const ProfileForm: React.FC<ProfileFormProps> = ({
form,
onSubmit,
}) => {
const {
register,
handleSubmit,
formState: { errors, isSubmitting },
} = form;
const { previewImage, isUploading, handleImageChange } = useImageUpload(form);


return (
<form
className="w-[60%] flex flex-col gap-6 rounded-md px-10 py-12 "
onSubmit={handleSubmit(onSubmit)}
>
<label className="text-xl font-semibold text-white pb-1">Edit Profile</label>
<hr className="w-32 mt-[-25px] border-2 border-green " />
<div className="w-full justify-between flex gap-16 flex-wrap">
<div className="flex flex-col flex-1 gap-2">
<label htmlFor="" className=" dark:text-white">
First Name
</label>
<input
type="text"
{...register("firstname")}
className="flex-1 pl-4 py-2 rounded border-[1px] border-black bg-transparent dark:text-white dark:border-white"
/>
{errors.firstname && (
<p className="text-sm text-red-600">{errors.firstname.message}</p>
)}
</div>
<div className="flex flex-col flex-1 gap-2">
<label htmlFor="" className="text-white">
Last Name
</label>
<input
type="text"
{...register("lastname")}
className="flex-1 pl-4 py-2 rounded-md border-[1px] bg-transparent dark:text-white"
/>
{errors.lastname && (
<p className="text-sm text-red-600">{errors.lastname.message}</p>
)}
</div>
</div>

<div className="w-full justify-between flex gap-16 flex-wrap">
<div className="flex-1 flex flex-col gap-2">
<label htmlFor="" className="text-white">
Code
</label>
<input
type="text"
{...register("code")}
className="flex-1 pl-4 py-2 rounded-md border-[1px] bg-transparent dark:text-white"
/>
{errors.code && (
<p className="text-sm text-red-600">{errors.code.message}</p>
)}
</div>
<div className="flex flex-1 flex-col gap-2">
<label htmlFor="" className="text-white">
Telephone
</label>
<input
type="text"
{...register("telephone")}
className="flex-1 pl-4 py-2 rounded-md border-[1px] bg-transparent dark:text-white"
/>
{errors.telephone && (
<p className="text-sm text-red-600">{errors.telephone.message}</p>
)}
</div>
</div>
<div className="w-full flex gap-16 justify-between flex-wrap ">
<div className="flex flex-1 flex-col gap-2">
<label htmlFor="" className="text-white">
Email
</label>
<input
type="text"
{...register("email")}
className="flex-1 pl-4 py-2 rounded-md border-[1px] bg-transparent dark:text-white"
/>
{errors.email && (
<p className="text-sm text-red-600">{errors.email.message}</p>
)}
</div>
<PasswordInput register={register} errors={errors} />
</div>

<div className="">
<input
type="file"
onChange={handleImageChange}
className="pl-4 py-2 rounded dark:bg-transparent dark:text-white"
/>
</div>
<div className="flex gap-4">
<button
className="py-2 mt-2 w-64 text-white font-medium bg-green rounded flex justify-center hover:bg-emerald-600"
disabled={isSubmitting}
>
{isSubmitting ? (
<ThreeDots height="20" width="30" color="#ffffff" />
) : (
"UPDATE"
)}
</button>
</div>
</form>
);
};

export default ProfileForm;
50 changes: 50 additions & 0 deletions src/components/updateUserProfile/imagePreview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import React from "react";
import { UseFormReturn } from "react-hook-form";
import { CiImageOff } from "react-icons/ci";
import { toast } from "react-toastify";
import { TuserSchema } from "../../utils/userSchema";

interface ImagePreviewProps {
previewImage: string | null;
form: UseFormReturn<TuserSchema>;
}

const DEFAULT_IMAGE =
"https://t4.ftcdn.net/jpg/01/24/65/69/360_F_124656969_x3y8YVzvrqFZyv3YLWNo6PJaC88SYxqM.jpg";

const ImagePreview: React.FC<ImagePreviewProps> = ({ previewImage, form }) => {
const { setValue, watch } = form;
const currentPicture = watch("picture");

const deletePreviewImage = () => {
if (currentPicture === DEFAULT_IMAGE) {
toast.info("This is the default image");
} else {
setValue("picture", DEFAULT_IMAGE);
toast.error("Image is removed");
}
};

if (!previewImage) return null;

return (
<div className="flex justify-between flex-wrap mt-5 relative">
<img
src={previewImage}
width={120}
height={120}
alt="Preview"
className="rounded-full"
/>
<button
className="w-36 h-10 self-end flex items-center gap-2 justify-center rounded-md text-white bg-red-700 hover:bg-red-500"
onClick={deletePreviewImage}
>
<CiImageOff size={22}/>
Delete Profile
</button>
</div>
);
};

export default ImagePreview;
36 changes: 36 additions & 0 deletions src/components/updateUserProfile/useImagePreview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { useState, useEffect, useCallback } from "react";
import { UseFormReturn } from "react-hook-form";
import {
handleFilePreview,
handleImageUpload,
} from "../../utils/imageuploadUtil";
import { TuserSchema } from "../../utils/userSchema";

const DEFAULT_IMAGE =
"https://t4.ftcdn.net/jpg/01/24/65/69/360_F_124656969_x3y8YVzvrqFZyv3YLWNo6PJaC88SYxqM.jpg";

const useImageUpload = (form: UseFormReturn<TuserSchema>) => {
const [previewImage, setPreviewImage] = useState<string | null>(null);
const [isUploading, setIsUploading] = useState(false);
const { setValue, watch } = form;
const currentPicture = watch("picture");

useEffect(() => {
setPreviewImage(currentPicture || DEFAULT_IMAGE);
}, [currentPicture]);

const handleImageChange = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (file) {
handleFilePreview(file, setPreviewImage);
handleImageUpload(file, setValue, setIsUploading);
}
},
[setValue]
);

return { previewImage, isUploading, handleImageChange, DEFAULT_IMAGE };
};

export default useImageUpload;
2 changes: 1 addition & 1 deletion src/pages/PageNotFound.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const PageNotFound = () => {
<Link to="/applicant">
<button>Go to Applicant Dashboard</button>
</Link>
) : role === "superAdmin" || role === "Admin" ? (
) : (role === "superAdmin" || role === "admin") ? (
<Link to="/admin">
<button>Go back to Homepage</button>
</Link>
Expand Down
Loading

0 comments on commit f8c3d66

Please sign in to comment.