Material-UI: Upload PDF file - reactjs

I have a ready-made template and in this template there is a file, which is the file below, and this code is through which you can select several images from the computer as shown in the image, but the problem is that I do not want to choose an image. I want to choose a PDF file and I tried to make Several changes such as changing the type of the selected file from image to file, but my problem was not resolved.
import { orange } from '#material-ui/core/colors';
import Icon from '#material-ui/core/Icon';
import { makeStyles } from '#material-ui/core/styles';
import clsx from 'clsx';
import FuseUtils from '#fuse/utils';
import { Controller, useFormContext } from 'react-hook-form';
function ProductImagesTab(props) {
const classes = useStyles(props);
const methods = useFormContext();
const { control, watch, setValue } = methods;
const images = watch('images');
return (
<div>
<div className="flex justify-center sm:justify-start flex-wrap -mx-16">
<Controller
name="images"
control={control}
render={({ field: { onChange, value } }) => (
<label
htmlFor="button-file"
className={clsx(
classes.productImageUpload,
'flex items-center justify-center relative w-128 h-128 rounded-16 mx-12 mb-24 overflow-hidden cursor-pointer shadow hover:shadow-lg'
)}
>
<input
accept="image/*"
className="hidden"
id="button-file"
type="file"
onChange={async (e) => {
function readFileAsync() {
return new Promise((resolve, reject) => {
const file = e.target.files[0];
if (!file) {
return;
}
const reader = new FileReader();
reader.onload = () => {
resolve({
id: FuseUtils.generateGUID(),
url: `data:${file.type};base64,${btoa(reader.result)}`,
type: 'image',
});
};
reader.onerror = reject;
reader.readAsBinaryString(file);
});
}
const newImage = await readFileAsync();
onChange([newImage, ...value]);
}}
/>
<Icon fontSize="large" color="action">
cloud_upload
</Icon>
</label>
)}
/>
<Controller
name="featuredImageId"
control={control}
defaultValue=""
render={({ field: { onChange, value } }) =>
images.map((media) => (
<div
onClick={() => onChange(media.id)}
onKeyDown={() => onChange(media.id)}
role="button"
tabIndex={0}
className={clsx(
classes.productImageItem,
'flex items-center justify-center relative w-128 h-128 rounded-16 mx-12 mb-24 overflow-hidden cursor-pointer outline-none shadow hover:shadow-lg',
media.id === value && 'featured'
)}
key={media.id}
>
<Icon className={classes.productImageFeaturedStar}>star</Icon>
<img className="max-w-none w-auto h-full" src={media.url} alt="product" />
</div>
))
}
/>
</div>
</div>
);
}
export default ProductImagesTab;

Looks like your Input HTML element is set to accept="image/*"
Instead you should have it accept pdf format like this <input type="file" accept="application/pdf">
https://www.w3schools.com/tags/tag_input.asp
Similar question answered here

Related

onSubmit does nothing what is wrong?

I am trying to add an article using a form
It's supposed to create it on a thirdweb collection but it doesn't do anything when i'm hitting the button
My IDE ( VScode ) highlights image, Props, and FormEvent
It used to highlight File too but it doesn't do it anymore after this command npm install --save-dev #types/node
the console.log doesn't return anything when I click I don't know if the problem is e.preventDefault(); but I would like some help
My Thirdweb is well configured and should return a window with metamask to confirm the transaction (the transaction consisting of mining an nft and this is the equivalent of an insert in a database)
I thank you for your attention
import React, { FormEvent, useState } from 'react';
import { useRouter } from 'next/router';
import Header from '../components/Header';
import { useAddress, useContract } from '#thirdweb-dev/react';
import Image from 'next/image';
import Placeholder from '../images/placeholder.png';
type Props = {
name: string;
description: string;
image: File;
}
function addItem({}: Props) {
const { contract } = useContract(
process.env.NEXT_PUBLIC_COLLECTION_CONTRACT,
"nft-collection"
);
const address = useAddress();
const router = useRouter();
const [preview, setPreview] = useState<string>();
const [image, setImage] = useState<File>();
const mintNft = async (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
if (!contract || !address) return;
console.log(contract, address, image);
if (!image) {
alert("Please select an image");
return;
}
const target = e.target as typeof e.target & {
name: { value: string };
description: { value: string };
};
const metadata = {
name: target.name.value,
description: target.description.value,
image: image,
};
try {
const tx = await contract.mintTo(address, metadata);
const receipt = tx.receipt; // the transaction receipt
const tokenId = tx.id; // the id of the NFT minted
const nft = await tx.data(); // (optional) fetch details of minted NFT
console.log(receipt, tokenId, nft);
await router.push("/");
} catch (error) {
console.error(error);
}
};
return (
<div>
<Header />
<main className="max-w-6xl mx-auto p-10 border">
<h1 className="text-4xl font-bold">Add an Item to the Marketplace</h1>
<h2 className="text-xl font-semibold pt-5">Item Details</h2>
<p className="pb-5">
By adding an item to the marketplace, you're essentially Minting an
NFT of the item into your wallet which we can then list for sale!
</p>
<div className="flex flex-col justify-center items-center md:flex-row md:space-x-5 pt-5">
<Image
className="border h-80 w-80 object-contain" // object-contain is the same as object-fit: contain / permet de ne pas déformer l'objet
alt="Placeholder"
src={preview || Placeholder}
width={500}
height={500}
/>
<form onSubmit={mintNft} className="flex flex-col flex-1 p-2 space-y-2" >
<label className="font-light">Name of Item</label>
<input
className="formField"
placeholder="Name of item..."
type="text"
name="name"
id="name"
/>
<label className="font-light">Description</label>
<input
className="formField"
placeholder="Enter Description..."
type="text"
name="description"
id="description"
/>
<label className="font-light">Image of the Item</label>
<input
type="file"
onChange={(e) => {
if (e.target.files?.[0]) {
setPreview(URL.createObjectURL(e.target.files[0]));
setImage(e.target.files[0]);
}
}}
/>
<button
type="submit"
className="bg-blue-600 font-fold text-white rounded-full py-4 px-10 w-56 mx-auto md:mt-auto md:ml-auto"
>
Add/Mint Item
</button>
</form>
</div>
</main>
</div>
)
}
export default addItem;

How to post a csv file using useForm from 'react-hook-form?

I got a query using RTK, and a component where I attach the file and submit - after I attach the file and submit as shown in the image below I get the file in a weird format such as: file: "[object FileList]" after console log it.
How was wondering how to post a csv file in the correct format, such as file.csv. My component looks like as follow:
import { FC, FormEventHandler, RefObject, useState } from 'react';
import { useForm } from 'react-hook-form';
import { yupResolver } from '#hookform/resolvers/yup';
import * as yup from 'yup';
import {
Dialog,
DialogPortal,
DialogOverlay,
DialogContent,
DialogTitle,
DialogClose
} from '#radix-ui/react-dialog';
import { DownloadIcon, XIcon } from '#heroicons/react/outline';
import { setFormErrors } from 'utils/formHelpers';
import InputField from 'components/ui/atoms/InputField';
import { useUploadBatchMutation } from 'redux/api/uploadBatchApi';
const schema = yup
.object({
description: yup.string().optional(),
file: yup.string().label('file').required()
})
.required();
interface Inputs extends yup.InferType<typeof schema> {
base: null;
}
interface Props {
open: boolean;
onClose: (value: boolean) => void;
triggerRef: RefObject<HTMLAnchorElement>;
}
const UploadBatchDialog: FC<Props> = ({ open, onClose, triggerRef }) => {
const [showLoading, setShowLoading] = useState<boolean>(false);
const {
handleSubmit,
register,
setError,
formState,
setValue,
reset: resetForm
} = useForm<Inputs>({
mode: 'onSubmit',
resolver: yupResolver(schema)
});
const [uploadBatch] = useUploadBatchMutation();
const onSubmit: FormEventHandler<HTMLFormElement> = async (event) => {
console.log('test');
event.preventDefault();
try {
await handleSubmit(async (data) => {
console.log(data);
const formData = new FormData();
formData.append('file', data.file);
if (data.description) {
formData.append('name', data.description);
}
uploadBatch(formData);
})(event);
} catch (error: any) {
setTimeout(
() =>
setFormErrors({
error,
setError,
fields: Object.keys(schema.fields)
}),
50
);
}
};
const formFailed = () => Object.keys(formState.errors).length > 0;
const onDialogClose = (value: boolean) => {
resetForm();
onClose(value);
};
const onCloseAutoFocus = () => triggerRef.current?.focus();
const [file, setFile] = useState<any>();
return (
<Dialog open={open} onOpenChange={onDialogClose}>
<DialogPortal>
<DialogOverlay className="bg-opacity/90 fixed inset-0 z-50 bg-inpay-green-700">
<DialogContent
onCloseAutoFocus={onCloseAutoFocus}
onInteractOutside={(e) => e.preventDefault()}
className="fixed top-[50%] left-[50%] translate-y-[-50%] translate-x-[-50%] rounded-lg shadow-[0_0_6px_#B7BCBA]"
>
<DialogTitle className="flex h-12 items-center justify-between rounded-t-lg border-b border-b-inpay-pumice bg-white px-6">
<DownloadIcon className="w-8 stroke-1 group-hover:stroke-1.5" />
<span className="pr-20">Upload batch file</span>
<DialogClose
aria-label="Close"
className="outline-none focus:outline-none"
>
<XIcon className="-m-1 h-8 w-8 outline-none focus:outline-none" />
</DialogClose>
</DialogTitle>
<div className="flex justify-center rounded-b-lg bg-inpay-black-haze-100 p-6">
<div className="w-72">
<form onSubmit={onSubmit} autoComplete="off">
<InputField
{...register('description')}
className="mb-4"
label="Name (if left blank the file name will be used)"
name="description"
/>
<div className="mb-4 flex flex-col">
<label className="mb-0.5 pl-2 text-xs">
Attach payout request batch file
</label>
<div className="relative flex h-12 w-full rounded-lg bg-white p-1 shadow-plain focus:shadow-plain-lg group-hover:shadow-plain-lg">
<input
className="absolute opacity-0"
type="file"
accept="text/csv"
{...register('file')}
onChange={(e) => {
if (e.target.files) {
setFile(e.target.files[0]);
} else {
setFile(null);
}
}}
/>
<span className="flex w-full items-center justify-between pl-2">
{file ? file.name : 'Please attach your file'}
<button className="inpay-button h-10">
Attach file
</button>
</span>
</div>
{/* <div>Create batch template</div> */}
</div>
<button
type="submit"
className="inpay-button w-full"
// disabled={
// formState.isSubmitSuccessful || !formState.isValid
// }
>
Upload batch
</button>
{formState.errors.base && (
<div
role="alert"
className="mt-2 rounded-2xl bg-rose-200 p-3 text-sm text-inpay-red-600"
style={{
filter: 'drop-shadow(0 0 1px rgb(0 0 0 / 0.16))'
}}
>
{formState.errors.base.message}
</div>
)}
</form>
</div>
</div>
</DialogContent>
</DialogOverlay>
</DialogPortal>
</Dialog>
);
};
export default UploadBatchDialog;
while my post request looks like this:
import { customerApi } from './customerApi';
interface UploadedFile {
file: any;
name: string;
}
export const uploadBatchApi = customerApi.injectEndpoints({
endpoints: (builder) => ({
uploadBatch: builder.mutation<any, any>({
query(form) {
return {
url: 'payouts/payout_request_csv_batches',
method: 'POST',
credentials: 'include',
body: form
};
}
})
})
});
export const { useUploadBatchMutation } = uploadBatchApi;
I am wondering what I did wrong and why react hook passes that strange string. Any clues would be much appreciated.

How to display a popup modal in React and getForms.io

I am having a problem in displaying a popup when subbmitting a data to getForms. It executes the data but i cannot display the modal. I need help.
`
import axios from'axios'
import {React ,useState} from 'react'
import { useForm} from 'react-hook-form'
import Modal from '../../Modal'
export default function ContactForm(){
const [serverState , setServerState] = useState({submitting: false, status: null})
const handleServerResponse = (ok, msg, form) => {
setServerState({ submitting: false, status: { ok, msg } })
if(ok){
console.log('msg')//THIS WORKS!!
<Modal/>//THIS DOESNT WORK!!
)
return
}
return
}
const { register, handleSubmit, watch, formState: { errors } } = useForm()
const onSubmit = (data,e) =>{
const formData = new FormData()
for(var field in data){
formData.append(field, data[field])
}
setServerState({submitting:true})
axios({
method:'post',
url:'https://getform.io/f/79e16760-4dbb-4e67-b212-c98e3f1b8eda',
data: formData
})
.then((r) => {
e.target.reset();
handleServerResponse(true, "Form Submitted",e)
})
.catch((r) => {
handleServerResponse(false, r.response.data.error, e);
})
}
return(
<section>
<form
onSubmit={handleSubmit(onSubmit)}
className='mt-5 gap-4 text-black flex flex-col'
>
<input className='pl-2 rounded-sm focus:outline-none' type='text' placeholder='Enter yourName' {...register("fullname")}/>
<input className='pl-2 rounded-sm focus:outline-none' type='email' placeholder='Enter your Email' {...register("email")}/>
<textarea className='pl-2 pt-2 rounded-sm focus:outline-none' rows={10} placeholder='Enter your Message'{...register('emailMessage')}/>
<input className=' text-white drop-shadow-lg rounded-lg bg-tertiary p-2 mx-auto' type='submit'/>
</form>
</section>
)
}
`
Im expecting a popup after the data was submitted or not submitted. it do works in console.log but not in React Component.
const [serverState , setServerState] = useState({submitting: false, status: null})
const handleServerResponse = (ok, msg, form) => {
setServerState({ submitting: false, status: { ok, msg } })
if(ok){
//console.log() works!!!!!
//React Component not working!!!!
return
}
//Same HERE!!
return
}
How do you find out that the React code is not working? It is not visible from the code you share. I only see the Modal import, but not its usage. Try logging inside the React component to see if it is rendered and if it is, then try increasing the z-index of the modal, so you can see it.
Ok, with your Modal added to the code I see two errors here, first you don't even return the Modal from this function and second, if you return it, you don't expect it anywhere in your code. Try to render the Modal conditionally in your section instead, like this:
return(
<section>
{showModal && (
<Modal />
)}
<form
onSubmit={handleSubmit(onSubmit)}
className='mt-5 gap-4 text-black flex flex-col'
>
...
</form>
</section>
)
Then add showModal variable into state like this:
const [showModal, setShowModal] = useState(false);
And finally change the handleServerResponce function like this:
if(ok){
console.log('msg')//THIS WORKS!!
setShowModal(true);
)
You will have to add close modal logic into your Modal after that.
And here is how the whole component will look like when you add also logic for passing the message to the modal:
import axios from'axios'
import {React, useState} from 'react'
import { useForm} from 'react-hook-form'
import Modal from '../../Modal'
export default function ContactForm(){
const [serverState , setServerState] = useState({submitting: false, status: null})
const [showModal, setShowModal] = useState(false);
const handleServerResponse = (ok, msg, formData) => {
setServerState({ submitting: false, status: { ok, msg } })
if(ok){
console.log('msg')//THIS WORKS!!
setShowModal(true);
)
return
}
return
}
const { register, handleSubmit, watch, formState: { errors } } = useForm()
const onSubmit = (data,e) =>{
const formData = new FormData()
for(var field in data){
formData.append(field, data[field])
}
setServerState({submitting:true})
axios({
method:'post',
url:'https://getform.io/f/79e16760-4dbb-4e67-b212-c98e3f1b8eda',
data: formData
})
.then((r) => {
e.target.reset();
handleServerResponse(true, "Form Submitted", r);
})
.catch((r) => {
handleServerResponse(false, r.response.data.error, e);
})
}
return(
<section>
{showModal && (
<Modal message={serverState.status ? serverState.status.msg : ""} />
)}
<form
onSubmit={handleSubmit(onSubmit)}
className='mt-5 gap-4 text-black flex flex-col'
>
<input className='pl-2 rounded-sm focus:outline-none' type='text' placeholder='Enter yourName' {...register("fullname")}/>
<input className='pl-2 rounded-sm focus:outline-none' type='email' placeholder='Enter your Email' {...register("email")}/>
<textarea className='pl-2 pt-2 rounded-sm focus:outline-none' rows={10} placeholder='Enter your Message'{...register('emailMessage')}/>
<input className=' text-white drop-shadow-lg rounded-lg bg-tertiary p-2 mx-auto' type='submit'/>
</form>
</section>
)
}

React values not updating through when rendering to child component

Please i need help
i am a beginer using react
I find it difficult get values and i just hope my explanation is clear
What will i do or which other approch is best for this
i want to get value={} from main <Controller> and pass it through render={} to be used in the child component <PhoneInput> the value={phone}
is an object that consist of arrays
this is the content of phone
[
{
country: 'bs',
phoneNumber: '122 234 4309',
label: 'Mobile',
},
{
country: 'bs',
phoneNumber: '898 776 9087',
label: 'Work',
},
];
the best of my knowledge is what i did but not it working
My aim is to enable user to add more phone number field by their self
i want a user to be able to delete field or add more phone field
Sample Image
<Controller
control={control}
name="phones"
value={phones} //phone is an array
render={({ field: { onChange, className, value, ref } }) => ( // Transfering value from value={phones} to use as value.map not working
<div className={clsx('mt-32', className)} ref={ref}>
{value.map((item, index) => ( //the value here becomes undefined
<PhoneInput
value={item}
key={index}
onChange={(val) => {
console.log(value);
onChange(value.map((_item, _index) => (index === _index ? val : _item)));
}}
onRemove={() => {
console.log(value);
onChange(value.filter((_item, _index) => index !== _index));
}}
hideRemove={value.length === 1}
/>
))}
<Button
className="group inline-flex items-center mt-2 -ml-4 py-2 px-4 rounded cursor-pointer"
onClick={() =>
onChange([...testPhone, Model().phoneNumbers[0]])
}
>
<span className="ml-8 font-medium text-secondary group-hover:underline">
Add a phone number
</span>
</Button>
</div>
)}
/>
if i send the value directly it works fine
<Controller
control={control}
name="phones"
value={phones}
render={({ field: { onChange, className, value, ref } }) => (
// <div className={clsx('mt-32', className)} ref={ref}>
{phones.map((item, index) => ( //Using variable phone directly
<PhoneInput
value={item}
key={index}
onChange={(val) => {
console.log(phones);
onChange(phones.map((_item, _index) => (index === _index ? val : _item)));
}}
onRemove={() => {
console.log(phones);
onChange(phones.filter((_item, _index) => index !== _index));
}}
hideRemove={phones.length === 1}
/>
))}
<Button
className="group inline-flex items-center mt-2 -ml-4 py-2 px-4 rounded cursor-pointer"
onClick={() =>
onChange([...phones, Model().phoneNumbers[0]])
}
>
<span className="ml-8 font-medium text-secondary group-hover:underline">
Add a phone number
</span>
</Button>
</div>
)}
/>
Here is the complete code
import { motion } from 'framer-motion';
import { useEffect, useState } from 'react';
import {
Button,
Card,
} from '#mui/material';
import clsx from 'clsx';
import { Controller, useForm } from 'react-hook-form';
import axios from 'axios';
import PhoneInput from '../../PhoneInput';
import Model from '../../Model';
/**
* Form Validation Schema
*/
const schema = yup.object().shape({
phone: yup.string().required('Phone is required'),
});
function Settings() {
const { control, watch, reset, handleSubmit, formState, getValues } = useForm({
mode: 'onChange',
resolver: yupResolver(schema),
});
const [phones, setPhones] = useState([]);
useEffect(() => {
axios.get('/api/v1/account').then((res) => {
if (res.data.success === true) {
setPhones(res.data.phones);
}
});
}, []);
return (
<Card
component={motion.div}
variants={IT}
className="w-full overflow-hidden w-full mb-32"
>
<div className=" p-24 w-full">
<Controller
control={control}
name="first_name"
render={({ field }) => (
<TextField
className="mt-32"
{...field}
label="First Name"
placeholder="First Name"
id="first_name"
error={!!errors.first_name}
helperText={errors?.first_name?.message}
variant="outlined"
required
fullWidth
/>
)}
/>
<Controller
control={control}
name="phones"
value={phones}
render={({ field: { onChange, className, value, ref } }) => (
<div className={clsx('mt-32', className)} ref={ref}>
{phones.map((item, index) => (
<PhoneInput
value={item}
key={index}
onChange={(val) => {
console.log(phones);
onChange(phones.map((_item, _index) => (index === _index ? val : _item)));
}}
onRemove={() => {
console.log(phones);
onChange(phones.filter((_item, _index) => index !== _index));
}}
hideRemove={phones.length === 1}
/>
))}
<Button
className="group inline-flex items-center mt-2 -ml-4 py-2 px-4 rounded cursor-pointer"
onClick={() =>
onChange([...phones, Model().phoneNumbers[0]])
}
>
<span className="ml-8 font-medium text-secondary group-hover:underline">
Add a phone number
</span>
</Button>
</div>
)}
/>
</div>
</Card>
);
}
export default Settings;
Thank you very much

Store Image files in an array based on index in React js fails. Image always gets added to single array item only

I'm trying to create an image upload window where users can upload product images based for different product variations.
The intended output looks like:
I've to store the image file and url linked to the variationId and variationOptionId that the rendered component belongs to.
However, no matter which "Add Image" button i click, images get added only to the first component (blue, in the above example).
The imageVariations state looks like:
variationId: '',
variationName: '',
variationOptionId: '',
variationOptionName: '',
images: [
{
file: null,
url: '',
}
],
Code of Parent Component:
import React, { useEffect, useState } from 'react';
import ItemContainer from '../../common/ItemContainer';
import ProductImageUpload from '../imageStockPrice/ProductImageUpload';
const Tab3ProductImages = ({ product, loading, setLoading }) => {
const [imageVariations, setImageVariations] = useState([]);
// loop over product.productVariations and get the variation
// where variation.variesImage = true
useEffect(() => {
let tempVar = []; // has variationID, variationOptionID, and images array
product &&
product.productVariations.forEach((item) => {
if (item.variation.variesImage) {
tempVar.push({
variationId: item.variationId,
variationName: item.variation.name,
variationOptionId: item.variationOptionId,
variationOptionName: item.variationOption.name,
images: [],
});
}
});
setImageVariations(tempVar);
}, [product]);
// console.log('imageVariations', imageVariations);
const imageUploadHandler = () => {};
const imageDefaultHandler = () => {};
const imageDeleteHandler = () => {};
return (
<div className="py-4">
<div className="space-y-4">
{imageVariations &&
imageVariations.map((imageVar, index) => (
<div key={index}>
<ItemContainer
title={`${imageVar.variationName}: ${imageVar.variationOptionName}`}
>
<ProductImageUpload
loading={loading}
index={index}
imageVar={imageVar}
setImageVariations={setImageVariations}
imageUploadHandler={imageUploadHandler}
/>
</ItemContainer>
</div>
))}
</div>
</div>
);
};
export default Tab3ProductImages;
Code of the Child component - ProductImageUpload:
import React from 'react';
import { RiImageAddFill } from 'react-icons/ri';
import { MdOutlineCancel } from 'react-icons/md';
import LoadingButton from '../../formComponents/LoadingButton';
const ProductImageUpload = ({
loading,
index,
imageVar,
setImageVariations,
imageUploadHandler,
}) => {
// Handling Images
const handleImageChange = (e) => {
const tempArr = [];
[...e.target.files].forEach((file) => {
tempArr.push({
file: file,
url: URL.createObjectURL(file),
});
});
setImageVariations((prevState) => {
const newState = [...prevState];
newState[index].images = [...newState[index].images, ...tempArr];
return newState;
});
};
const removeImageFromList = (e) => {
setImageVariations((prevState) => {
const newState = [...prevState];
newState[index].images = newState[index].images.filter((item) => {
return item.url !== e.target.src;
});
return newState;
});
};
return (
<div className="space-y-4 grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="my-auto">
<form onSubmit={imageUploadHandler}>
<div className="flex gap-4">
<label
htmlFor="categoryImages"
className="block w-full py-1 px-2 rounded cursor-pointer border
border-violet-700 bg-violet-50 hover:bg-violet-100 text-violet-700"
>
<span className="flex items-center gap-2">
<RiImageAddFill />
<span className="text-sm">Click to Add Images</span>
<span className="font-barlow text-sm">(max 10 images)</span>
</span>
<input
type="file"
id="categoryImages"
accept="image/*"
multiple
onChange={handleImageChange}
className="sr-only"
/>
</label>
{loading ? (
<LoadingButton />
) : (
<button className="text-sm py-1 px-4 rounded cursor-pointer border
border-blue-700 bg-blue-50 hover:bg-blue-100 text-blue-700">
Upload
</button>
)}
</div>
</form>
</div>
{/* upload progress bar */}
<div className="flex items-center">
<div className="bg-gray-200 w-full h-4 rounded-full overflow-hidden">
<div
className="h-4 bg-violet-500 text-xs font-medium text-center p-0.5
leading-none rounded-full transition-all duration-75"
style={{ width: `${progress}%` }}
>
<span
className={`ml-2 ${
progress === 0 ? 'text-gray-600' : 'text-blue-100'
}`}
>
{progress}%
</span>
</div>
</div>
</div>
{/* upload progress bar ends */}
{/* image preview section */}
<div>
<ul className="flex flex-wrap gap-2">
{imageVar &&
imageVar.images.length > 0 &&
imageVar.images.map((item, index) => (
<li key={index} className="relative">
<img
src={item.url}
alt="preview"
className="w-20 h-20 object-cover rounded shadow-lg border
hover:scale-110 transition duration-200"
/>
<button
onClick={removeImageFromList}
className="absolute -top-2 -right-2"
>
<MdOutlineCancel className="text-red-400 bg-white" />
</button>
</li>
))}
</ul>
</div>
{/* image preview section ends */}
</div>
);
};
export default ProductImageUpload;

Resources