'use client'; import * as Yup from 'yup'; import React from 'react'; import PropTypes from 'prop-types'; import toast from 'react-hot-toast'; import { capitalCase } from 'change-case'; import { useRouter } from 'next-nprogress-bar'; import { Form, FormikProvider, useFormik } from 'formik'; // mui import { styled } from '@mui/material/styles'; import { LoadingButton } from '@mui/lab'; import { Card, Chip, Grid, Stack, Select, TextField, Typography, FormControl, Autocomplete, FormHelperText, FormControlLabel, FormGroup, Skeleton, Switch, InputAdornment } from '@mui/material'; // api import * as api from 'src/services'; import { useMutation } from 'react-query'; import axios from 'axios'; // components import UploadMultiFile from 'src/components/upload/UploadMultiFile'; import { fCurrency } from 'src/utils/formatNumber'; // ---------------------------------------------------------------------- const GENDER_OPTION = ['men', 'women', 'kids', 'others']; const STATUS_OPTIONS = ['sale', 'new', 'regular', 'disabled']; const LabelStyle = styled(Typography)(({ theme }) => ({ ...theme.typography.subtitle2, color: theme.palette.text.secondary, lineHeight: 2.5 })); // ---------------------------------------------------------------------- export default function ProductForm({ categories, currentProduct, categoryLoading = false, isInitialized = false, brands, shops, isVendor }) { const router = useRouter(); const [loading, setloading] = React.useState(false); const { mutate, isLoading: updateLoading } = useMutation( currentProduct ? 'update' : 'new', currentProduct ? isVendor ? api.updateVendorProduct : api.updateProductByAdmin : isVendor ? api.createVendorProduct : api.createProductByAdmin, { onSuccess: (data) => { toast.success(data.message); router.push((isVendor ? '/vendor' : '/admin') + '/products'); }, onError: (error) => { toast.error(error.response.data.message); } } ); const NewProductSchema = Yup.object().shape({ name: Yup.string().required('Product name is required'), code: Yup.string().required('Product code is required'), // tags: Yup.array().min(1, 'Tags is required'), status: Yup.string().required('Status is required'), // description: Yup.string().required('Description is required'), category: Yup.string().required('Category is required'), shop: isVendor ? Yup.string().nullable().notRequired() : Yup.string().required('Shop is required'), subCategory: Yup.string().required('Sub Category is required'), slug: Yup.string().required('Slug is required'), // brand: Yup.string().required('brand is required'), // metaTitle: Yup.string().required('Meta title is required'), // metaDescription: Yup.string().required('Meta description is required'), images: Yup.array().min(1, 'Images is required'), // sku: Yup.string().required('Sku is required'), available: Yup.number().required('Quantaty is required') // colors: Yup.array().required('Color is required'), // sizes: Yup.array().required('Size is required') // price: Yup.number().required('Price is required'), // priceSale: Yup.number() // .required('Sale price is required') // .lessThan(Yup.ref('price'), 'Sale price should be smaller than price') }); const formik = useFormik({ enableReinitialize: true, initialValues: { name: currentProduct?.name || '', description: currentProduct?.description || '', code: currentProduct?.code || '', slug: currentProduct?.slug || '', metaTitle: currentProduct?.metaTitle || '', metaDescription: currentProduct?.metaDescription || '', brand: currentProduct?.brand || brands[0]?._id || 'brand', tags: currentProduct?.tags || [], gender: currentProduct?.gender || '', category: currentProduct?.category || (categories.length && categories[0]?._id) || '', shop: isVendor ? null : currentProduct?.shop || (shops?.length && shops[0]?._id) || '', subCategory: currentProduct?.subCategory || (categories.length && categories[0].subCategories[0]?._id) || '', status: currentProduct?.status || STATUS_OPTIONS[0], blob: currentProduct?.blob || [], isFeatured: currentProduct?.isFeatured || true, sku: currentProduct?.sku || '', price: currentProduct?.price || '', priceSale: currentProduct?.priceSale || '', colors: currentProduct?.colors || '', sizes: currentProduct?.sizes || '', available: currentProduct?.available || '', images: currentProduct?.images || [] }, validationSchema: NewProductSchema, onSubmit: async (values) => { const { ...rest } = values; try { mutate({ ...rest, priceSale: values.priceSale || values.price, ...(currentProduct && { currentSlug: currentProduct.slug }) }); } catch (error) { console.error(error); } } }); const { errors, values, touched, handleSubmit, setFieldValue, getFieldProps } = formik; const { mutate: deleteMutate } = useMutation(api.singleDeleteFile, { onError: (error) => { toast.error(error.response.data.message); } }); // handle drop const handleDrop = (acceptedFiles) => { setloading(true); const uploaders = acceptedFiles.map((file) => { const formData = new FormData(); formData.append('file', file); formData.append('upload_preset', 'my-uploads'); setFieldValue('blob', values.blob.concat(acceptedFiles)); return axios.post(`https://api.cloudinary.com/v1_1/${process.env.CLOUDINARY_CLOUD_NAME}/image/upload`, formData); }); axios.all(uploaders).then((data) => { const newImages = data.map(({ data }) => ({ url: data.secure_url, _id: data.public_id // blob: blobs[i], })); setloading(false); setFieldValue('images', values.images.concat(newImages)); }); }; // handleAddVariants // handleRemoveAll const handleRemoveAll = () => { values.images.forEach((image) => { deleteMutate(image._id); }); setFieldValue('images', []); }; // handleRemove const handleRemove = (file) => { const removeImage = values.images.filter((_file) => { if (_file._id === file._id) { deleteMutate(file._id); } return _file !== file; }); setFieldValue('images', removeImage); }; const handleTitleChange = (event) => { const title = event.target.value; const slug = title .toLowerCase() // .replace(/[^a-zA-Z0-9\s]+/g, '') .replace(/\s+/g, '-'); // convert to lowercase, remove special characters, and replace spaces with hyphens formik.setFieldValue('slug', slug); // set the value of slug in the formik state formik.handleChange(event); // handle the change in formik }; return (
{isInitialized ? ( ) : ( {'Product Name'} )} {isInitialized ? ( ) : ( )}
{isVendor ? null : ( {isInitialized ? ( ) : ( {'Shop'} )} {touched.shop && errors.shop && ( {touched.shop && errors.shop} )} )} {isInitialized ? ( ) : ( {'Category'} )} {!categoryLoading ? ( ) : ( )} {touched.category && errors.category && ( {touched.category && errors.category} )} {isInitialized ? ( ) : ( {'Sub Category'} )} {!categoryLoading ? ( ) : ( )} {touched.subCategory && errors.subCategory && ( {touched.subCategory && errors.subCategory} )} {isInitialized ? ( ) : ( {'Brand'} )} {touched.brand && errors.brand && ( {touched.brand && errors.brand} )} {'Sizes'} { setFieldValue('sizes', newValue); }} options={[]} renderTags={(value, getTagProps) => value.map((option, index) => ( )) } renderInput={(params) => ( )} /> {'Colors'} { setFieldValue('colors', newValue); }} options={[]} renderTags={(value, getTagProps) => value.map((option, index) => ( )) } renderInput={(params) => ( )} /> {isInitialized ? ( ) : ( {'Gender'} )} {isInitialized ? ( ) : ( )} {isInitialized ? ( ) : ( {'Status'} )} {isInitialized ? ( ) : ( )} {touched.status && errors.status && ( {touched.status && errors.status} )}
{isInitialized ? ( ) : ( {'Product Code'} )} {isInitialized ? ( ) : ( )}
{'Product Sku'}
{isInitialized ? ( ) : ( {'Tags'} )} {isInitialized ? ( ) : ( { setFieldValue('tags', newValue); }} options={[]} renderTags={(value, getTagProps) => value.map((option, index) => ( )) } renderInput={(params) => ( )} /> )}
{isInitialized ? ( ) : ( {'Meta Title'} )} {isInitialized ? ( ) : ( )}
{isInitialized ? ( ) : ( {'Description'}{' '} )} {isInitialized ? ( ) : ( )}
{'Products Images'} 1080 * 1080 {touched.images && errors.images && ( {touched.images && errors.images} )}
{isInitialized ? ( ) : ( {'Slug'} )} {isInitialized ? ( ) : ( )}
{isInitialized ? ( ) : ( {'Meta Description'}{' '} )} {isInitialized ? ( ) : ( )}
{'Quantity'}
{'Regular Price'} {fCurrency(0)?.split('0')[0]}, type: 'number' }} error={Boolean(touched.price && errors.price)} helperText={touched.price && errors.price} />
{'Sale Price'} {fCurrency(0)?.split('0')[0]}, type: 'number' }} error={Boolean(touched.priceSale && errors.priceSale)} helperText={touched.priceSale && errors.priceSale} />
setFieldValue('isFeatured', e.target.checked)} checked={values.isFeatured} /> } label={'Featured Product'} />
{isInitialized ? ( ) : ( {currentProduct ? 'Update Product' : 'Create Product'} )}
); } ProductForm.propTypes = { categories: PropTypes.arrayOf( PropTypes.shape({ _id: PropTypes.string.isRequired, name: PropTypes.string.isRequired, subCategories: PropTypes.array.isRequired // ... add other required properties for category }) ).isRequired, currentProduct: PropTypes.shape({ _id: PropTypes.string, name: PropTypes.string, description: PropTypes.string, code: PropTypes.string, slug: PropTypes.string, metaTitle: PropTypes.string, metaDescription: PropTypes.string, brand: PropTypes.string, tags: PropTypes.arrayOf(PropTypes.string), gender: PropTypes.string, category: PropTypes.string, subCategory: PropTypes.string, status: PropTypes.string, blob: PropTypes.array, isFeatured: PropTypes.bool, sku: PropTypes.string, price: PropTypes.number, priceSale: PropTypes.number, colors: PropTypes.arrayOf(PropTypes.string), sizes: PropTypes.arrayOf(PropTypes.string), available: PropTypes.number, images: PropTypes.array // ... add other optional properties for currentProduct }), categoryLoading: PropTypes.bool, isInitialized: PropTypes.bool, isVendor: PropTypes.bool, brands: PropTypes.arrayOf( PropTypes.shape({ _id: PropTypes.string.isRequired, name: PropTypes.string.isRequired // ... add other required properties for brands }) ) };