refactor(auth, layouts): enhance UI components for improved aesthetics and usability
- Updated SignIn and Profile components with new background styles and layout adjustments for a more modern look. - Refined button styles in GoogleSigninButton and SigninWithPassword for better user interaction feedback. - Enhanced InputGroup and Select components with improved styling and error handling for a more consistent user experience. - Adjusted Sidebar and Header components for better responsiveness and visual appeal. - Implemented spotlight effect in UserInfo dropdown for a more engaging user interface.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
"use client";
|
||||
import InputGroup from "@/components/FormElements/InputGroup";
|
||||
import { ShowcaseSection } from "@/components/Layouts/showcase-section";
|
||||
import FileImageUploadCard from "@/components/ui/FileImageUploadCard";
|
||||
import FormFooter from "@/components/ui/FormFooter";
|
||||
import { REGEX } from "@/constants/regex";
|
||||
import { FormErrorMessages } from "@/constants/Texts";
|
||||
@@ -13,7 +14,19 @@ interface IProps {
|
||||
id?: string;
|
||||
}
|
||||
const CreateAdminForm: FC<IProps> = ({ defaultValues, editMode, id }) => {
|
||||
const { errors, handleSubmit, register, onSubmit } = useCreateAdminPresenter({
|
||||
const {
|
||||
errors,
|
||||
handleSubmit,
|
||||
register,
|
||||
onSubmit,
|
||||
isUploadingImage,
|
||||
uploadProgress,
|
||||
selectedFileName,
|
||||
selectedFileSize,
|
||||
previewUrl,
|
||||
clearSelectedProfileImage,
|
||||
profileImageRegister,
|
||||
} = useCreateAdminPresenter({
|
||||
editMode,
|
||||
defaultValues,
|
||||
id,
|
||||
@@ -221,20 +234,18 @@ const CreateAdminForm: FC<IProps> = ({ defaultValues, editMode, id }) => {
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<InputGroup
|
||||
type="file"
|
||||
fileStyleVariant="style1"
|
||||
label="Attach file"
|
||||
register={register}
|
||||
name="profile_image"
|
||||
/>
|
||||
{errors.profile_image && (
|
||||
<p className="mt-1 text-sm text-red-500">
|
||||
{errors.profile_image.message as string}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<FileImageUploadCard
|
||||
label="Profile image"
|
||||
inputId="profile-image-upload"
|
||||
fileRegister={profileImageRegister}
|
||||
isLoading={isUploadingImage}
|
||||
uploadProgress={uploadProgress}
|
||||
previewUrl={previewUrl}
|
||||
selectedFileName={selectedFileName}
|
||||
selectedFileSize={selectedFileSize}
|
||||
onClear={clearSelectedProfileImage}
|
||||
errorMessage={errors.profile_image?.message as string | undefined}
|
||||
/>
|
||||
|
||||
<div className="md:col-span-2">
|
||||
<FormFooter />
|
||||
|
||||
@@ -1,37 +1,171 @@
|
||||
import { useCreateAdmin, useUpdateAdmin } from "@/hooks/queries";
|
||||
import { ICreateAdminCredentials } from "@/lib/api";
|
||||
import { ICreateAdminCredentials, userService } from "@/lib/api";
|
||||
import { validateImageFile } from "@/utils/shared";
|
||||
import { useIsMutating } from "@tanstack/react-query";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { ChangeEvent, useEffect, useState } from "react";
|
||||
import { SubmitHandler, useForm } from "react-hook-form";
|
||||
import { toast } from "react-toastify";
|
||||
|
||||
interface IProps {
|
||||
editMode?: boolean;
|
||||
defaultValues?: ICreateAdminCredentials;
|
||||
id?: string;
|
||||
}
|
||||
|
||||
type CreateAdminFormValues = Omit<ICreateAdminCredentials, "profile_image"> & {
|
||||
profile_image?: FileList;
|
||||
};
|
||||
|
||||
const useCreateAdminPresenter = ({
|
||||
editMode = false,
|
||||
defaultValues,
|
||||
id,
|
||||
}: IProps) => {
|
||||
const isMutating = useIsMutating();
|
||||
const [selectedFileName, setSelectedFileName] = useState<string>("");
|
||||
const [selectedFileSize, setSelectedFileSize] = useState<number | null>(null);
|
||||
const [isUploadingImage, setIsUploadingImage] = useState(false);
|
||||
const [uploadProgress, setUploadProgress] = useState<number>(0);
|
||||
const [uploadedProfileImageUrl, setUploadedProfileImageUrl] = useState<
|
||||
string | undefined
|
||||
>(defaultValues?.profile_image);
|
||||
const [previewUrl, setPreviewUrl] = useState<string | null>(
|
||||
defaultValues?.profile_image ?? null,
|
||||
);
|
||||
const {
|
||||
register,
|
||||
formState: { errors },
|
||||
handleSubmit,
|
||||
watch,
|
||||
} = useForm<ICreateAdminCredentials>({
|
||||
setError,
|
||||
clearErrors,
|
||||
setValue,
|
||||
} = useForm<CreateAdminFormValues>({
|
||||
mode: "onChange",
|
||||
defaultValues,
|
||||
defaultValues: {
|
||||
...defaultValues,
|
||||
profile_image: undefined,
|
||||
},
|
||||
});
|
||||
|
||||
const { mutate: createAdmin } = useCreateAdmin();
|
||||
const { mutate: updateAdmin } = useUpdateAdmin(id as string);
|
||||
const onSubmit: SubmitHandler<ICreateAdminCredentials> = (data) => {
|
||||
|
||||
const resetToDefaultProfileImage = () => {
|
||||
setSelectedFileName("");
|
||||
setSelectedFileSize(null);
|
||||
setUploadProgress(0);
|
||||
setUploadedProfileImageUrl(defaultValues?.profile_image);
|
||||
setPreviewUrl(defaultValues?.profile_image ?? null);
|
||||
};
|
||||
|
||||
const validateProfileImage = (file: File): string | null => {
|
||||
return validateImageFile({ file });
|
||||
};
|
||||
|
||||
const prepareUploadState = (file: File) => {
|
||||
clearErrors("profile_image");
|
||||
setSelectedFileName(file.name);
|
||||
setSelectedFileSize(file.size);
|
||||
setIsUploadingImage(true);
|
||||
setUploadProgress(0);
|
||||
};
|
||||
|
||||
const handleUploadSuccess = (downloadUrl: string) => {
|
||||
setUploadedProfileImageUrl(downloadUrl);
|
||||
setPreviewUrl(downloadUrl);
|
||||
setUploadProgress(100);
|
||||
};
|
||||
|
||||
const handleUploadFailure = (input: HTMLInputElement) => {
|
||||
setUploadedProfileImageUrl(undefined);
|
||||
resetToDefaultProfileImage();
|
||||
setValue("profile_image", undefined);
|
||||
setError("profile_image", {
|
||||
type: "validate",
|
||||
message: "Failed to upload profile image",
|
||||
});
|
||||
input.value = "";
|
||||
};
|
||||
|
||||
const uploadProfileImage = async (file: File) => {
|
||||
return userService.uploadProfileImage({
|
||||
file,
|
||||
onProgress: (percent) => {
|
||||
setUploadProgress(percent);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const onProfileImageChange = async (event: ChangeEvent<HTMLInputElement>) => {
|
||||
const selectedFile = event.target.files?.[0];
|
||||
const inputElement = event.target;
|
||||
|
||||
if (!selectedFile) {
|
||||
resetToDefaultProfileImage();
|
||||
clearErrors("profile_image");
|
||||
return;
|
||||
}
|
||||
|
||||
const validationError = validateProfileImage(selectedFile);
|
||||
if (validationError) {
|
||||
setError("profile_image", {
|
||||
type: "validate",
|
||||
message: validationError,
|
||||
});
|
||||
inputElement.value = "";
|
||||
return;
|
||||
}
|
||||
|
||||
prepareUploadState(selectedFile);
|
||||
|
||||
try {
|
||||
const downloadUrl = await uploadProfileImage(selectedFile);
|
||||
handleUploadSuccess(downloadUrl);
|
||||
} catch {
|
||||
handleUploadFailure(inputElement);
|
||||
} finally {
|
||||
setIsUploadingImage(false);
|
||||
}
|
||||
};
|
||||
|
||||
const clearSelectedProfileImage = () => {
|
||||
setValue("profile_image", undefined);
|
||||
resetToDefaultProfileImage();
|
||||
clearErrors("profile_image");
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (previewUrl?.startsWith("blob:")) {
|
||||
URL.revokeObjectURL(previewUrl);
|
||||
}
|
||||
};
|
||||
}, [previewUrl]);
|
||||
|
||||
const profileImageRegister = register("profile_image", {
|
||||
onChange: onProfileImageChange,
|
||||
});
|
||||
|
||||
const onSubmit: SubmitHandler<CreateAdminFormValues> = async (data) => {
|
||||
const file = data.profile_image?.[0];
|
||||
const profileImageUrl = uploadedProfileImageUrl ?? defaultValues?.profile_image;
|
||||
|
||||
if (file && !profileImageUrl) {
|
||||
toast.error("Please wait for profile image upload to finish");
|
||||
return;
|
||||
}
|
||||
|
||||
const payload: ICreateAdminCredentials = {
|
||||
...data,
|
||||
profile_image: profileImageUrl,
|
||||
};
|
||||
|
||||
if (editMode) {
|
||||
updateAdmin({ id: id as string, credentials: data });
|
||||
updateAdmin({ id: id as string, credentials: payload });
|
||||
} else {
|
||||
createAdmin({ ...data, role: "admin" });
|
||||
createAdmin({ ...payload, role: "admin" });
|
||||
}
|
||||
};
|
||||
const router = useRouter();
|
||||
@@ -43,6 +177,13 @@ const useCreateAdminPresenter = ({
|
||||
isMutating,
|
||||
onSubmit,
|
||||
watchForm: watch,
|
||||
isUploadingImage,
|
||||
uploadProgress,
|
||||
selectedFileName,
|
||||
selectedFileSize,
|
||||
previewUrl,
|
||||
clearSelectedProfileImage,
|
||||
profileImageRegister,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user