fix: add product

This commit is contained in:
ferdiansyah783 2025-08-05 21:31:53 +07:00
parent 0fde122ac4
commit 799837e82e
14 changed files with 393 additions and 68 deletions

View File

@ -0,0 +1,49 @@
// MUI Imports
import Grid from '@mui/material/Grid2'
// Component Imports
import ProductAddHeader from '@views/apps/ecommerce/products/add/ProductAddHeader'
import ProductInformation from '@views/apps/ecommerce/products/add/ProductInformation'
import ProductImage from '@views/apps/ecommerce/products/add/ProductImage'
import ProductVariants from '@views/apps/ecommerce/products/add/ProductVariants'
import ProductInventory from '@views/apps/ecommerce/products/add/ProductInventory'
import ProductPricing from '@views/apps/ecommerce/products/add/ProductPricing'
import ProductOrganize from '@views/apps/ecommerce/products/add/ProductOrganize'
const eCommerceProductsEdit = () => {
return (
<Grid container spacing={6}>
<Grid size={{ xs: 12 }}>
<ProductAddHeader />
</Grid>
<Grid size={{ xs: 12, md: 8 }}>
<Grid container spacing={6}>
<Grid size={{ xs: 12 }}>
<ProductInformation />
</Grid>
<Grid size={{ xs: 12 }}>
<ProductImage />
</Grid>
<Grid size={{ xs: 12 }}>
<ProductVariants />
</Grid>
<Grid size={{ xs: 12 }}>
<ProductInventory />
</Grid>
</Grid>
</Grid>
<Grid size={{ xs: 12, md: 4 }}>
<Grid container spacing={6}>
<Grid size={{ xs: 12 }}>
<ProductPricing />
</Grid>
<Grid size={{ xs: 12 }}>
<ProductOrganize />
</Grid>
</Grid>
</Grid>
</Grid>
)
}
export default eCommerceProductsEdit

View File

@ -14,8 +14,6 @@ import { getLocalizedUrl } from '../utils/i18n'
export default function AuthGuard({ children, locale }: ChildrenType & { locale: Locale }) { export default function AuthGuard({ children, locale }: ChildrenType & { locale: Locale }) {
const { isAuthenticated } = useAuth() const { isAuthenticated } = useAuth()
console.log('isAuthenticated', isAuthenticated)
useEffect(() => { useEffect(() => {
if (!isAuthenticated) { if (!isAuthenticated) {
redirect(getLocalizedUrl('/login', locale)) redirect(getLocalizedUrl('/login', locale))

View File

@ -6,13 +6,15 @@ import chatReducer from '@/redux-store/slices/chat'
import calendarReducer from '@/redux-store/slices/calendar' import calendarReducer from '@/redux-store/slices/calendar'
import kanbanReducer from '@/redux-store/slices/kanban' import kanbanReducer from '@/redux-store/slices/kanban'
import emailReducer from '@/redux-store/slices/email' import emailReducer from '@/redux-store/slices/email'
import productReducer from '@/redux-store/slices/product'
export const store = configureStore({ export const store = configureStore({
reducer: { reducer: {
chatReducer, chatReducer,
calendarReducer, calendarReducer,
kanbanReducer, kanbanReducer,
emailReducer emailReducer,
productReducer
}, },
middleware: getDefaultMiddleware => getDefaultMiddleware({ serializableCheck: false }) middleware: getDefaultMiddleware => getDefaultMiddleware({ serializableCheck: false })
}) })

View File

@ -0,0 +1,47 @@
// Third-party Imports
import type { Draft, PayloadAction } from '@reduxjs/toolkit'
import { createSlice } from '@reduxjs/toolkit'
// Type Imports
// Data Imports
import { ProductRequest } from '../../types/services/product'
const initialState: { productRequest: ProductRequest } = {
productRequest: {
category_id: '',
sku: '',
name: '',
description: '',
barcode: '',
price: 0,
cost: 0,
printer_type: '',
image_url: '',
variants: []
}
}
export const productSlice = createSlice({
name: 'product',
initialState,
reducers: {
setProductField: <K extends keyof ProductRequest>(
state: Draft<{ productRequest: ProductRequest }>,
action: PayloadAction<{ field: K; value: ProductRequest[K] }>
) => {
const { field, value } = action.payload
state.productRequest[field] = value
},
setProduct: (state, action: PayloadAction<ProductRequest>) => {
state.productRequest = action.payload
},
resetProduct: state => {
state.productRequest = initialState.productRequest
}
}
})
export const { setProductField, setProduct, resetProduct } = productSlice.actions
export default productSlice.reducer

View File

@ -0,0 +1,25 @@
import { useMutation } from '@tanstack/react-query'
import { api } from '../api'
import { toast } from 'react-toastify'
export const useFilesMutation = {
uploadFile: () => {
return useMutation({
mutationFn: async (newFile: FormData) => {
const response = await api.post('/files/upload', newFile, {
headers: {
'Content-Type': 'multipart/form-data'
}
})
return response.data.data
},
onSuccess: data => {
toast.success('File uploaded successfully!')
},
onError: (error: any) => {
toast.error(error.response.data.errors[0].cause)
}
})
}
}

View File

@ -0,0 +1,21 @@
import { useMutation } from '@tanstack/react-query'
import { api } from '../api'
import { toast } from 'react-toastify'
import { ProductRequest } from '../../types/services/product'
export const useProductsMutation = {
createProduct: () => {
return useMutation({
mutationFn: async (newProduct: ProductRequest) => {
const response = await api.post('/products', newProduct)
return response.data
},
onSuccess: data => {
toast.success('Product created successfully!')
},
onError: (error: any) => {
toast.error(error.response.data.errors[0].cause)
}
})
}
}

View File

@ -35,3 +35,22 @@ export type Products = {
limit: number limit: number
total_pages: number total_pages: number
} }
export type ProductVariantRequest = {
name: string
price_modifier: number
cost: number
}
export type ProductRequest = {
category_id: string
sku: string
name: string
description: string
barcode: string
price: number
cost: number
printer_type: string
image_url: string
variants: ProductVariantRequest[]
}

View File

@ -329,7 +329,16 @@ const CourseTable = ({ courseData }: { courseData?: Course[] }) => {
</table> </table>
</div> </div>
<TablePagination <TablePagination
component={() => <TablePaginationComponent table={table} />} component={() => (
<TablePaginationComponent
pageIndex={table.getState().pagination.pageIndex + 1}
pageSize={table.getState().pagination.pageSize}
totalCount={table.getFilteredRowModel().rows.length}
onPageChange={(_, page) => {
table.setPageIndex(page - 1)
}}
/>
)}
count={table.getFilteredRowModel().rows.length} count={table.getFilteredRowModel().rows.length}
rowsPerPage={table.getState().pagination.pageSize} rowsPerPage={table.getState().pagination.pageSize}
page={table.getState().pagination.pageIndex} page={table.getState().pagination.pageIndex}

View File

@ -1,8 +1,30 @@
'use client'
// MUI Imports // MUI Imports
import Button from '@mui/material/Button' import Button from '@mui/material/Button'
import Typography from '@mui/material/Typography' import Typography from '@mui/material/Typography'
import { useProductsMutation } from '../../../../../services/mutations/products'
import { useDispatch, useSelector } from 'react-redux'
import { RootState } from '../../../../../redux-store'
import { CircularProgress } from '@mui/material'
import { resetProduct } from '../../../../../redux-store/slices/product'
const ProductAddHeader = () => { const ProductAddHeader = () => {
const dispatch = useDispatch()
const { mutate, isPending } = useProductsMutation.createProduct()
const { productRequest } = useSelector((state: RootState) => state.productReducer)
const handleSubmit = () => {
const { cost, price, ...rest } = productRequest
const newProductRequest = { ...rest, cost: Number(cost), price: Number(price) }
mutate(newProductRequest, {
onSuccess: () => {
dispatch(resetProduct())
}
})
}
return ( return (
<div className='flex flex-wrap sm:items-center justify-between max-sm:flex-col gap-6'> <div className='flex flex-wrap sm:items-center justify-between max-sm:flex-col gap-6'>
<div> <div>
@ -16,7 +38,9 @@ const ProductAddHeader = () => {
Discard Discard
</Button> </Button>
<Button variant='tonal'>Save Draft</Button> <Button variant='tonal'>Save Draft</Button>
<Button variant='contained'>Publish Product</Button> <Button variant='contained' disabled={isPending} onClick={handleSubmit}>
Publish Product {isPending && <CircularProgress color='inherit' size={16} className='ml-2' />}
</Button>
</div> </div>
</div> </div>
) )

View File

@ -4,16 +4,16 @@
import { useState } from 'react' import { useState } from 'react'
// MUI Imports // MUI Imports
import Card from '@mui/material/Card' import type { BoxProps } from '@mui/material/Box'
import CardHeader from '@mui/material/CardHeader'
import CardContent from '@mui/material/CardContent'
import Button from '@mui/material/Button' import Button from '@mui/material/Button'
import Card from '@mui/material/Card'
import CardContent from '@mui/material/CardContent'
import CardHeader from '@mui/material/CardHeader'
import IconButton from '@mui/material/IconButton' import IconButton from '@mui/material/IconButton'
import List from '@mui/material/List' import List from '@mui/material/List'
import ListItem from '@mui/material/ListItem' import ListItem from '@mui/material/ListItem'
import Typography from '@mui/material/Typography' import Typography from '@mui/material/Typography'
import { styled } from '@mui/material/styles' import { styled } from '@mui/material/styles'
import type { BoxProps } from '@mui/material/Box'
// Third-party Imports // Third-party Imports
import { useDropzone } from 'react-dropzone' import { useDropzone } from 'react-dropzone'
@ -24,6 +24,9 @@ import CustomAvatar from '@core/components/mui/Avatar'
// Styled Component Imports // Styled Component Imports
import AppReactDropzone from '@/libs/styles/AppReactDropzone' import AppReactDropzone from '@/libs/styles/AppReactDropzone'
import { useDispatch } from 'react-redux'
import { useFilesMutation } from '../../../../../services/mutations/files'
import { setProductField } from '../../../../../redux-store/slices/product'
type FileProp = { type FileProp = {
name: string name: string
@ -46,9 +49,27 @@ const Dropzone = styled(AppReactDropzone)<BoxProps>(({ theme }) => ({
})) }))
const ProductImage = () => { const ProductImage = () => {
const dispatch = useDispatch()
const { mutate, isPending } = useFilesMutation.uploadFile()
// States // States
const [files, setFiles] = useState<File[]>([]) const [files, setFiles] = useState<File[]>([])
const handleUpload = () => {
if (!files.length) return
const formData = new FormData()
formData.append('file', files[0])
formData.append('file_type', 'image')
formData.append('description', 'Product image')
mutate(formData, {
onSuccess: data => {
dispatch(setProductField({ field: 'image_url', value: data.file_url }))
}
})
}
// Hooks // Hooks
const { getRootProps, getInputProps } = useDropzone({ const { getRootProps, getInputProps } = useDropzone({
onDrop: (acceptedFiles: File[]) => { onDrop: (acceptedFiles: File[]) => {
@ -129,7 +150,9 @@ const ProductImage = () => {
<Button color='error' variant='tonal' onClick={handleRemoveAllFiles}> <Button color='error' variant='tonal' onClick={handleRemoveAllFiles}>
Remove All Remove All
</Button> </Button>
<Button variant='contained'>Upload Files</Button> <Button variant='contained' onClick={handleUpload} disabled={isPending}>
{isPending ? 'Uploading...' : 'Upload'}
</Button>
</div> </div>
</> </>
) : null} ) : null}

View File

@ -24,6 +24,11 @@ import CustomTextField from '@core/components/mui/TextField'
// Style Imports // Style Imports
import '@/libs/styles/tiptapEditor.css' import '@/libs/styles/tiptapEditor.css'
import { useDispatch, useSelector } from 'react-redux'
import { RootState } from '../../../../../redux-store'
import { setProductField } from '@/redux-store/slices/product'
import { useEffect } from 'react'
const EditorToolbar = ({ editor }: { editor: Editor | null }) => { const EditorToolbar = ({ editor }: { editor: Editor | null }) => {
if (!editor) { if (!editor) {
return null return null
@ -114,6 +119,13 @@ const EditorToolbar = ({ editor }: { editor: Editor | null }) => {
} }
const ProductInformation = () => { const ProductInformation = () => {
const dispatch = useDispatch()
const { name, sku, barcode, description } = useSelector((state: RootState) => state.productReducer.productRequest)
const handleInputChange = (field: any, value: any) => {
dispatch(setProductField({ field, value }))
}
const editor = useEditor({ const editor = useEditor({
extensions: [ extensions: [
StarterKit, StarterKit,
@ -128,24 +140,57 @@ const ProductInformation = () => {
immediatelyRender: false, immediatelyRender: false,
content: ` content: `
<p> <p>
Keep your account secure with authentication step. ${description}
</p> </p>
` `
}) })
useEffect(() => {
if (!editor) return
const updateListener = () => {
const html = editor.getHTML()
dispatch(setProductField({ field: 'description', value: html }))
}
editor.on('update', updateListener)
return () => {
editor.off('update', updateListener)
}
}, [editor])
return ( return (
<Card> <Card>
<CardHeader title='Product Information' /> <CardHeader title='Product Information' />
<CardContent> <CardContent>
<Grid container spacing={6} className='mbe-6'> <Grid container spacing={6} className='mbe-6'>
<Grid size={{ xs: 12 }}> <Grid size={{ xs: 12 }}>
<CustomTextField fullWidth label='Product Name' placeholder='iPhone 14' /> <CustomTextField
fullWidth
label='Product Name'
placeholder='iPhone 14'
value={name}
onChange={e => handleInputChange('name', e.target.value)}
/>
</Grid> </Grid>
<Grid size={{ xs: 12, sm: 6 }}> <Grid size={{ xs: 12, sm: 6 }}>
<CustomTextField fullWidth label='SKU' placeholder='FXSK123U' /> <CustomTextField
fullWidth
label='SKU'
placeholder='FXSK123U'
value={sku}
onChange={e => handleInputChange('sku', e.target.value)}
/>
</Grid> </Grid>
<Grid size={{ xs: 12, sm: 6 }}> <Grid size={{ xs: 12, sm: 6 }}>
<CustomTextField fullWidth label='Barcode' placeholder='0123-4567' /> <CustomTextField
fullWidth
label='Barcode'
placeholder='0123-4567'
value={barcode}
onChange={e => handleInputChange('barcode', e.target.value)}
/>
</Grid> </Grid>
</Grid> </Grid>
<Typography className='mbe-1'>Description (Optional)</Typography> <Typography className='mbe-1'>Description (Optional)</Typography>

View File

@ -1,48 +1,56 @@
'use client' 'use client'
// React Imports // React Imports
import { useState } from 'react'
// MUI Imports // MUI Imports
import Card from '@mui/material/Card' import Card from '@mui/material/Card'
import CardHeader from '@mui/material/CardHeader'
import CardContent from '@mui/material/CardContent' import CardContent from '@mui/material/CardContent'
import CardHeader from '@mui/material/CardHeader'
import MenuItem from '@mui/material/MenuItem' import MenuItem from '@mui/material/MenuItem'
// Component Imports // Component Imports
import CustomIconButton from '@core/components/mui/IconButton' import CustomIconButton from '@core/components/mui/IconButton'
import CustomTextField from '@core/components/mui/TextField' import CustomTextField from '@core/components/mui/TextField'
import { useDispatch, useSelector } from 'react-redux'
import { RootState } from '../../../../../redux-store'
import { setProductField } from '../../../../../redux-store/slices/product'
import { useCategoriesQuery } from '../../../../../services/queries/categories'
import { Category } from '../../../../../types/services/category'
const ProductOrganize = () => { const ProductOrganize = () => {
// States const dispatch = useDispatch()
const [vendor, setVendor] = useState('') const { category_id, printer_type } = useSelector((state: RootState) => state.productReducer.productRequest)
const [category, setCategory] = useState('')
const [collection, setCollection] = useState('') const { data: categoriesApi } = useCategoriesQuery.getCategories()
const [status, setStatus] = useState('')
const handleSelectChange = (field: any, value: any) => {
dispatch(setProductField({ field, value }))
}
return ( return (
<Card> <Card>
<CardHeader title='Organize' /> <CardHeader title='Organize' />
<CardContent> <CardContent>
<form onSubmit={e => e.preventDefault()} className='flex flex-col gap-6'> <form onSubmit={e => e.preventDefault()} className='flex flex-col gap-6'>
<CustomTextField select fullWidth label='Vendor' value={vendor} onChange={e => setVendor(e.target.value)}>
<MenuItem value={`Men's Clothing`}>Men&apos;s Clothing</MenuItem>
<MenuItem value={`Women's Clothing`}>Women&apos;s Clothing</MenuItem>
<MenuItem value={`Kid's Clothing`}>Kid&apos;s Clothing</MenuItem>
</CustomTextField>
<div className='flex items-end gap-4'> <div className='flex items-end gap-4'>
<CustomTextField <CustomTextField
select select
fullWidth fullWidth
label='Category' label='Category'
value={category} value={category_id}
onChange={e => setCategory(e.target.value)} onChange={e => handleSelectChange('category_id', e.target.value)}
> >
<MenuItem value='Household'>Household</MenuItem> {categoriesApi?.categories.length ? (
<MenuItem value='Office'>Office</MenuItem> categoriesApi?.categories.map((item: Category, index: number) => (
<MenuItem value='Electronics'>Electronics</MenuItem> <MenuItem key={index} value={item.id}>
<MenuItem value='Management'>Management</MenuItem> {item.name}
<MenuItem value='Automotive'>Automotive</MenuItem> </MenuItem>
))
) : (
<MenuItem disabled value=''>
Loading categories...
</MenuItem>
)}
</CustomTextField> </CustomTextField>
<CustomIconButton variant='tonal' color='primary' className='min-is-fit'> <CustomIconButton variant='tonal' color='primary' className='min-is-fit'>
<i className='tabler-plus' /> <i className='tabler-plus' />
@ -51,20 +59,18 @@ const ProductOrganize = () => {
<CustomTextField <CustomTextField
select select
fullWidth fullWidth
label='Collection' label='Printer Type'
value={collection} value={printer_type}
onChange={e => setCollection(e.target.value)} onChange={e => handleSelectChange('printer_type', e.target.value)}
> >
<MenuItem value={`Men's Clothing`}>Men&apos;s Clothing</MenuItem> <MenuItem value={`kitchen`}>Kitchen</MenuItem>
<MenuItem value={`Women's Clothing`}>Women&apos;s Clothing</MenuItem>
<MenuItem value={`Kid's Clothing`}>Kid&apos;s Clothing</MenuItem>
</CustomTextField> </CustomTextField>
<CustomTextField select fullWidth label='Status' value={status} onChange={e => setStatus(e.target.value)}> {/* <CustomTextField select fullWidth label='Status' value={status} onChange={e => setStatus(e.target.value)}>
<MenuItem value='Published'>Published</MenuItem> <MenuItem value='Published'>Published</MenuItem>
<MenuItem value='Inactive'>Inactive</MenuItem> <MenuItem value='Inactive'>Inactive</MenuItem>
<MenuItem value='Scheduled'>Scheduled</MenuItem> <MenuItem value='Scheduled'>Scheduled</MenuItem>
</CustomTextField> </CustomTextField>
<CustomTextField fullWidth label='Enter Tags' placeholder='Fashion, Trending, Summer' /> <CustomTextField fullWidth label='Enter Tags' placeholder='Fashion, Trending, Summer' /> */}
</form> </form>
</CardContent> </CardContent>
</Card> </Card>

View File

@ -1,3 +1,5 @@
'use client'
// MUI Imports // MUI Imports
import Card from '@mui/material/Card' import Card from '@mui/material/Card'
import CardHeader from '@mui/material/CardHeader' import CardHeader from '@mui/material/CardHeader'
@ -11,15 +13,39 @@ import Typography from '@mui/material/Typography'
// Component Imports // Component Imports
import Form from '@components/Form' import Form from '@components/Form'
import CustomTextField from '@core/components/mui/TextField' import CustomTextField from '@core/components/mui/TextField'
import { useDispatch, useSelector } from 'react-redux'
import { RootState } from '../../../../../redux-store'
import { setProductField } from '../../../../../redux-store/slices/product'
const ProductPricing = () => { const ProductPricing = () => {
const { price, cost } = useSelector((state: RootState) => state.productReducer.productRequest)
const dispatch = useDispatch()
const handleInputChange = (field: any, value: any) => {
dispatch(setProductField({ field, value }))
}
return ( return (
<Card> <Card>
<CardHeader title='Pricing' /> <CardHeader title='Pricing' />
<CardContent> <CardContent>
<Form> <Form>
<CustomTextField fullWidth label='Base Price' placeholder='Enter Base Price' className='mbe-6' /> <CustomTextField
<CustomTextField fullWidth label='Discounted Price' placeholder='$499' className='mbe-6' /> fullWidth
label='Base Price'
placeholder='Enter Base Price'
className='mbe-6'
value={price}
onChange={e => handleInputChange('price', e.target.value)}
/>
<CustomTextField
fullWidth
label='Cost'
placeholder='$499'
className='mbe-6'
value={cost}
onChange={e => handleInputChange('cost', e.target.value)}
/>
<FormControlLabel control={<Checkbox defaultChecked />} label='Charge tax on this product' /> <FormControlLabel control={<Checkbox defaultChecked />} label='Charge tax on this product' />
<Divider className='mlb-2' /> <Divider className='mlb-2' />
<div className='flex items-center justify-between'> <div className='flex items-center justify-between'>

View File

@ -1,30 +1,42 @@
'use client' 'use client'
// React Imports // React Imports
import { useState } from 'react'
import type { SyntheticEvent } from 'react'
// MUI Imports // MUI Imports
import Grid from '@mui/material/Grid2'
import Card from '@mui/material/Card'
import CardHeader from '@mui/material/CardHeader'
import CardContent from '@mui/material/CardContent'
import Button from '@mui/material/Button' import Button from '@mui/material/Button'
import MenuItem from '@mui/material/MenuItem' import Card from '@mui/material/Card'
import CardContent from '@mui/material/CardContent'
import CardHeader from '@mui/material/CardHeader'
import Grid from '@mui/material/Grid2'
// Components Imports // Components Imports
import CustomIconButton from '@core/components/mui/IconButton' import CustomIconButton from '@core/components/mui/IconButton'
import CustomTextField from '@core/components/mui/TextField' import CustomTextField from '@core/components/mui/TextField'
import { useDispatch, useSelector } from 'react-redux'
import { RootState } from '../../../../../redux-store'
import { setProductField } from '../../../../../redux-store/slices/product'
const ProductVariants = () => { const ProductVariants = () => {
// States const dispatch = useDispatch()
const [count, setCount] = useState(1) const { variants } = useSelector((state: RootState) => state.productReducer.productRequest)
const deleteForm = (e: SyntheticEvent) => { const handleAddVariant = () => {
e.preventDefault() dispatch(setProductField({ field: 'variants', value: [...variants, { name: '', cost: 0, price_modifier: 0 }] }))
}
// @ts-ignore const handleRemoveVariant = (index: number) => {
e.target.closest('.repeater-item').remove() const updated = variants.filter((_, i) => i !== index)
dispatch(setProductField({ field: 'variants', value: updated }))
}
const handleInputChange = (index: number, e: any) => {
const { name, value } = e.target
const updated = [...variants]
updated[index] = {
...updated[index],
[name]: name === 'name' ? value : Number(value)
}
dispatch(setProductField({ field: 'variants', value: updated }))
} }
return ( return (
@ -32,21 +44,40 @@ const ProductVariants = () => {
<CardHeader title='Product Variants' /> <CardHeader title='Product Variants' />
<CardContent> <CardContent>
<Grid container spacing={6}> <Grid container spacing={6}>
{Array.from(Array(count).keys()).map((item, index) => ( {variants.map((variant, index) => (
<Grid key={index} size={{ xs: 12 }} className='repeater-item'> <Grid key={index} size={{ xs: 12 }} className='repeater-item'>
<Grid container spacing={6}> <Grid container spacing={6}>
<Grid size={{ xs: 12, sm: 4 }}> <Grid size={{ xs: 12, sm: 4 }}>
<CustomTextField select fullWidth label='Options' defaultValue='Size'> <CustomTextField
<MenuItem value='Size'>Size</MenuItem> fullWidth
<MenuItem value='Color'>Color</MenuItem> label='Variant Name'
<MenuItem value='Weight'>Weight</MenuItem> placeholder='Hot'
<MenuItem value='Smell'>Smell</MenuItem> name='name'
</CustomTextField> value={variant.name}
onChange={e => handleInputChange(index, e)}
/>
</Grid> </Grid>
<Grid size={{ xs: 12, sm: 8 }} alignSelf='end'> <Grid size={{ xs: 12, sm: 4 }}>
<div className='flex items-center gap-6'> <CustomTextField
<CustomTextField fullWidth placeholder='Enter Variant Value' /> fullWidth
<CustomIconButton onClick={deleteForm} className='min-is-fit'> label='Price Modifier'
placeholder='0'
name='price_modifier'
value={variant.price_modifier}
onChange={e => handleInputChange(index, e)}
/>
</Grid>
<Grid size={{ xs: 12, sm: 4 }}>
<div className='flex items-center gap-3'>
<CustomTextField
fullWidth
label='Cost'
placeholder='0'
name='cost'
value={variant.cost}
onChange={e => handleInputChange(index, e)}
/>
<CustomIconButton onClick={() => handleRemoveVariant(index)} className='min-is-fit'>
<i className='tabler-x' /> <i className='tabler-x' />
</CustomIconButton> </CustomIconButton>
</div> </div>
@ -55,7 +86,7 @@ const ProductVariants = () => {
</Grid> </Grid>
))} ))}
<Grid size={{ xs: 12 }}> <Grid size={{ xs: 12 }}>
<Button variant='contained' onClick={() => setCount(count + 1)} startIcon={<i className='tabler-plus' />}> <Button variant='contained' onClick={handleAddVariant} startIcon={<i className='tabler-plus' />}>
Add Another Option Add Another Option
</Button> </Button>
</Grid> </Grid>