chore: add new forniture page
This commit is contained in:
		| @@ -7,6 +7,7 @@ import MenuDrawerPrivate, { OPEN_WIDTH, MINI_WIDTH } from './components/MenuDraw | ||||
| import Footer from './components/Footer'; | ||||
| import Dashboard from './private/dashboard/Dashboard'; | ||||
| import UserManagement from './private/users/UserManagement'; | ||||
| import FurnitureVariantManagement from './private/fornitures/FurnitureVariantManagement'; | ||||
| import LoginPage from './private/LoginPage'; | ||||
| import { Routes, Route, Navigate } from 'react-router-dom'; | ||||
| import { useAuth } from './context/AuthContext'; | ||||
| @@ -66,6 +67,7 @@ export default function App() { | ||||
|               <PrivateRoute> | ||||
|                 {zone === 'public' && currentView === 'Dashboard' && <Dashboard />} | ||||
|                 {zone === 'public' && currentView === '/Users/UserManagement' && <UserManagement />} | ||||
|                 {zone === 'public' && currentView === '/ProductsManagement/CatalogManagement/ProductCollections' && <FurnitureVariantManagement />} | ||||
|               </PrivateRoute> | ||||
|             } | ||||
|           /> | ||||
|   | ||||
							
								
								
									
										55
									
								
								src/api/furnitureVariantApi.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								src/api/furnitureVariantApi.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,55 @@ | ||||
| export default class FurnitureVariantApi { | ||||
|   constructor(token) { | ||||
|     this.baseUrl = 'https://inventory-bff.dream-views.com/api/v1/FurnitureVariant'; | ||||
|     this.token = token;  | ||||
|   } | ||||
|  | ||||
|   headers(json = true) { | ||||
|     return { | ||||
|       accept: 'application/json', | ||||
|       ...(json ? { 'Content-Type': 'application/json' } : {}), | ||||
|       ...(this.token ? { Authorization: `Bearer ${this.token}` } : {}), | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   async getAllVariants() { | ||||
|     const res = await fetch(`${this.baseUrl}/GetAll`, { | ||||
|       method: 'GET', | ||||
|       headers: this.headers(false), | ||||
|     }); | ||||
|     if (!res.ok) throw new Error(`GetAll error ${res.status}: ${await res.text()}`); | ||||
|     return res.json(); | ||||
|   } | ||||
|  | ||||
|   // Assuming similar endpoints; adjust names if backend differs. | ||||
|   async createVariant(payload) { | ||||
|     const res = await fetch(`${this.baseUrl}/Create`, { | ||||
|       method: 'POST', | ||||
|       headers: this.headers(), | ||||
|       body: JSON.stringify(payload), | ||||
|     }); | ||||
|     if (!res.ok) throw new Error(`Create error ${res.status}: ${await res.text()}`); | ||||
|     return res.json(); | ||||
|   } | ||||
|  | ||||
|   async updateVariant(payload) { | ||||
|     const res = await fetch(`${this.baseUrl}/Update`, { | ||||
|       method: 'PUT', | ||||
|       headers: this.headers(), | ||||
|       body: JSON.stringify(payload), | ||||
|     }); | ||||
|     if (!res.ok) throw new Error(`Update error ${res.status}: ${await res.text()}`); | ||||
|     return res.json(); | ||||
|   } | ||||
|  | ||||
|   async deleteVariant(payload) { | ||||
|     // If your API is soft-delete via Update status, reuse updateVariant. | ||||
|     const res = await fetch(`${this.baseUrl}/Delete`, { | ||||
|       method: 'DELETE', | ||||
|       headers: this.headers(), | ||||
|       body: JSON.stringify(payload), | ||||
|     }); | ||||
|     if (!res.ok) throw new Error(`Delete error ${res.status}: ${await res.text()}`); | ||||
|     return res.json(); | ||||
|   } | ||||
| } | ||||
| @@ -155,6 +155,8 @@ export default function MenuDrawerPrivate({ | ||||
|                             } else { | ||||
|                                 if (node.title === 'Users Management') { | ||||
|                                     onSelect?.('/Users/UserManagement'); | ||||
|                                 } else if (node.title === 'Product Collections') { | ||||
|                                     onSelect?.('/ProductsManagement/CatalogManagement/ProductCollections'); | ||||
|                                 } else { | ||||
|                                     onSelect?.(node.title); | ||||
|                                 } | ||||
|   | ||||
							
								
								
									
										181
									
								
								src/private/fornitures/AddOrEditFurnitureVariantForm.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										181
									
								
								src/private/fornitures/AddOrEditFurnitureVariantForm.jsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,181 @@ | ||||
| // src/private/furniture/AddOrEditFurnitureVariantForm.jsx | ||||
| import { useEffect, useMemo, useState } from 'react'; | ||||
| import { Box, Button, TextField, MenuItem, Grid } from '@mui/material'; | ||||
| import FurnitureVariantApi from '../../api/furnitureVariantApi'; | ||||
| import { useAuth } from '../../context/AuthContext'; | ||||
|  | ||||
| export default function AddOrEditFurnitureVariantForm({ initialData, onAdd, onCancel }) { | ||||
|   const { user } = useAuth(); | ||||
|   const token = user?.thalosToken || localStorage.getItem('thalosToken'); | ||||
|   const api = useMemo(() => (new FurnitureVariantApi(token)), [token]); | ||||
|  | ||||
|   const [form, setForm] = useState({ | ||||
|     _Id: '', | ||||
|     modelId: '', | ||||
|     name: '', | ||||
|     color: '', | ||||
|     line: '', | ||||
|     stock: 0, | ||||
|     price: 0, | ||||
|     currency: 'USD', | ||||
|     categoryId: '', | ||||
|     providerId: '', | ||||
|     attributes: { material: '', legs: '', origin: '' }, | ||||
|     status: 'Active', | ||||
|   }); | ||||
|  | ||||
|   useEffect(() => { | ||||
|     if (initialData) { | ||||
|       setForm({ | ||||
|         _Id: initialData._id || initialData._Id || '', | ||||
|         modelId: initialData.modelId ?? '', | ||||
|         name: initialData.name ?? '', | ||||
|         color: initialData.color ?? '', | ||||
|         line: initialData.line ?? '', | ||||
|         stock: initialData.stock ?? 0, | ||||
|         price: initialData.price ?? 0, | ||||
|         currency: initialData.currency ?? 'USD', | ||||
|         categoryId: initialData.categoryId ?? '', | ||||
|         providerId: initialData.providerId ?? '', | ||||
|         attributes: { | ||||
|           material: initialData?.attributes?.material ?? '', | ||||
|           legs: initialData?.attributes?.legs ?? '', | ||||
|           origin: initialData?.attributes?.origin ?? '', | ||||
|         }, | ||||
|         status: initialData.status ?? 'Active', | ||||
|       }); | ||||
|     } else { | ||||
|       setForm({ | ||||
|         _Id: '', | ||||
|         modelId: '', | ||||
|         name: '', | ||||
|         color: '', | ||||
|         line: '', | ||||
|         stock: 0, | ||||
|         price: 0, | ||||
|         currency: 'USD', | ||||
|         categoryId: '', | ||||
|         providerId: '', | ||||
|         attributes: { material: '', legs: '', origin: '' }, | ||||
|         status: 'Active', | ||||
|       }); | ||||
|     } | ||||
|   }, [initialData]); | ||||
|  | ||||
|   const setVal = (name, value) => setForm((p) => ({ ...p, [name]: value })); | ||||
|   const setAttr = (name, value) => setForm((p) => ({ ...p, attributes: { ...p.attributes, [name]: value } })); | ||||
|  | ||||
|   const handleSubmit = async () => { | ||||
|     try { | ||||
|       if (form._Id) { | ||||
|         // UPDATE | ||||
|         const payload = { | ||||
|           _Id: form._Id, | ||||
|           modelId: form.modelId, | ||||
|           name: form.name, | ||||
|           color: form.color, | ||||
|           line: form.line, | ||||
|           stock: Number(form.stock) || 0, | ||||
|           price: Number(form.price) || 0, | ||||
|           currency: form.currency, | ||||
|           categoryId: form.categoryId, | ||||
|           providerId: form.providerId, | ||||
|           attributes: { | ||||
|             material: form.attributes.material, | ||||
|             legs: form.attributes.legs, | ||||
|             origin: form.attributes.origin, | ||||
|           }, | ||||
|           status: form.status, | ||||
|         }; | ||||
|         await api.updateVariant(payload); | ||||
|       } else { | ||||
|         // CREATE | ||||
|         const payload = { | ||||
|           modelId: form.modelId, | ||||
|           name: form.name, | ||||
|           color: form.color, | ||||
|           line: form.line, | ||||
|           stock: Number(form.stock) || 0, | ||||
|           price: Number(form.price) || 0, | ||||
|           currency: form.currency, | ||||
|           categoryId: form.categoryId, | ||||
|           providerId: form.providerId, | ||||
|           attributes: { | ||||
|             material: form.attributes.material, | ||||
|             legs: form.attributes.legs, | ||||
|             origin: form.attributes.origin, | ||||
|           }, | ||||
|           status: form.status, | ||||
|         }; | ||||
|         await api.createVariant(payload); | ||||
|       } | ||||
|       onAdd?.(); | ||||
|     } catch (err) { | ||||
|       console.error('Submit variant failed:', err); | ||||
|     } | ||||
|   }; | ||||
|  | ||||
|   return ( | ||||
|     <Box sx={{ py: 2 }}> | ||||
|       <Grid container spacing={2}> | ||||
|         <Grid item xs={12} md={6}> | ||||
|           <TextField fullWidth label="Model Id" value={form.modelId} onChange={(e) => setVal('modelId', e.target.value)} /> | ||||
|         </Grid> | ||||
|         <Grid item xs={12} md={6}> | ||||
|           <TextField fullWidth label="Name" value={form.name} onChange={(e) => setVal('name', e.target.value)} /> | ||||
|         </Grid> | ||||
|  | ||||
|         <Grid item xs={12} md={4}> | ||||
|           <TextField fullWidth label="Color" value={form.color} onChange={(e) => setVal('color', e.target.value)} /> | ||||
|         </Grid> | ||||
|         <Grid item xs={12} md={4}> | ||||
|           <TextField fullWidth label="Line" value={form.line} onChange={(e) => setVal('line', e.target.value)} /> | ||||
|         </Grid> | ||||
|         <Grid item xs={12} md={2}> | ||||
|           <TextField fullWidth type="number" label="Stock" value={form.stock} onChange={(e) => setVal('stock', e.target.value)} /> | ||||
|         </Grid> | ||||
|         <Grid item xs={12} md={2}> | ||||
|           <TextField fullWidth type="number" label="Price" value={form.price} onChange={(e) => setVal('price', e.target.value)} /> | ||||
|         </Grid> | ||||
|  | ||||
|         <Grid item xs={12} md={3}> | ||||
|           <TextField fullWidth label="Currency" value={form.currency} onChange={(e) => setVal('currency', e.target.value)} /> | ||||
|         </Grid> | ||||
|         <Grid item xs={12} md={4}> | ||||
|           <TextField fullWidth label="Category Id" value={form.categoryId} onChange={(e) => setVal('categoryId', e.target.value)} /> | ||||
|         </Grid> | ||||
|         <Grid item xs={12} md={5}> | ||||
|           <TextField fullWidth label="Provider Id" value={form.providerId} onChange={(e) => setVal('providerId', e.target.value)} /> | ||||
|         </Grid> | ||||
|  | ||||
|         <Grid item xs={12} md={4}> | ||||
|           <TextField fullWidth label="Material" value={form.attributes.material} onChange={(e) => setAttr('material', e.target.value)} /> | ||||
|         </Grid> | ||||
|         <Grid item xs={12} md={4}> | ||||
|           <TextField fullWidth label="Legs" value={form.attributes.legs} onChange={(e) => setAttr('legs', e.target.value)} /> | ||||
|         </Grid> | ||||
|         <Grid item xs={12} md={4}> | ||||
|           <TextField fullWidth label="Origin" value={form.attributes.origin} onChange={(e) => setAttr('origin', e.target.value)} /> | ||||
|         </Grid> | ||||
|  | ||||
|         <Grid item xs={12} md={4}> | ||||
|           <TextField | ||||
|             fullWidth | ||||
|             select | ||||
|             label="Status" | ||||
|             value={form.status} | ||||
|             onChange={(e) => setVal('status', e.target.value)} | ||||
|           > | ||||
|             <MenuItem value="Active">Active</MenuItem> | ||||
|             <MenuItem value="Inactive">Inactive</MenuItem> | ||||
|           </TextField> | ||||
|         </Grid> | ||||
|       </Grid> | ||||
|  | ||||
|       <Box display="flex" justifyContent="flex-end" mt={3} gap={1}> | ||||
|         <Button onClick={onCancel} className="button-transparent">Cancel</Button> | ||||
|         <Button variant="contained" onClick={handleSubmit} className="button-gold">Save</Button> | ||||
|       </Box> | ||||
|     </Box> | ||||
|   ); | ||||
| } | ||||
							
								
								
									
										254
									
								
								src/private/fornitures/FurnitureVariantManagement.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										254
									
								
								src/private/fornitures/FurnitureVariantManagement.jsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,254 @@ | ||||
| import SectionContainer from '../../components/SectionContainer'; | ||||
| import { useEffect, useRef, useState } from 'react'; | ||||
| import { DataGrid } from '@mui/x-data-grid'; | ||||
| import { | ||||
|   Typography, Button, Dialog, DialogTitle, DialogContent, | ||||
|   IconButton, Box | ||||
| } from '@mui/material'; | ||||
| import EditRoundedIcon from '@mui/icons-material/EditRounded'; | ||||
| import DeleteRoundedIcon from '@mui/icons-material/DeleteRounded'; | ||||
| import AddOrEditFurnitureVariantForm from './AddOrEditFurnitureVariantForm'; | ||||
| import FurnitureVariantApi from '../../api/furnitureVariantApi'; | ||||
| import { useAuth } from '../../context/AuthContext'; | ||||
| import useApiToast from '../../hooks/useApiToast'; | ||||
|  | ||||
| const columnsBase = [ | ||||
|   { field: 'modelId', headerName: 'Model Id', width: 260 }, | ||||
|   { field: 'name', headerName: 'Name', width: 220 }, | ||||
|   { field: 'color', headerName: 'Color', width: 160 }, | ||||
|   { field: 'line', headerName: 'Line', width: 160 }, | ||||
|   { field: 'stock', headerName: 'Stock', width: 100, type: 'number' }, | ||||
|   { field: 'price', headerName: 'Price', width: 120, type: 'number', | ||||
|     valueFormatter: (p) => p?.value != null ? Number(p.value).toFixed(2) : '—' | ||||
|   }, | ||||
|   { field: 'currency', headerName: 'Currency', width: 120 }, | ||||
|   { field: 'categoryId', headerName: 'Category Id', width: 280 }, | ||||
|   { field: 'providerId', headerName: 'Provider Id', width: 280 }, | ||||
|   { | ||||
|     field: 'attributes.material', | ||||
|     headerName: 'Material', | ||||
|     width: 160, | ||||
|     valueGetter: (p) => p?.row?.attributes?.material ?? '—' | ||||
|   }, | ||||
|   { | ||||
|     field: 'attributes.legs', | ||||
|     headerName: 'Legs', | ||||
|     width: 160, | ||||
|     valueGetter: (p) => p?.row?.attributes?.legs ?? '—' | ||||
|   }, | ||||
|   { | ||||
|     field: 'attributes.origin', | ||||
|     headerName: 'Origin', | ||||
|     width: 160, | ||||
|     valueGetter: (p) => p?.row?.attributes?.origin ?? '—' | ||||
|   }, | ||||
|   { field: 'status', headerName: 'Status', width: 120 }, | ||||
|   { | ||||
|     field: 'createdAt', | ||||
|     headerName: 'Created At', | ||||
|     width: 180, | ||||
|     valueFormatter: (p) => p?.value ? new Date(p.value).toLocaleString() : '—' | ||||
|   }, | ||||
|   { field: 'createdBy', headerName: 'Created By', width: 160, valueGetter: (p) => p?.row?.createdBy ?? '—' }, | ||||
|   { | ||||
|     field: 'updatedAt', | ||||
|     headerName: 'Updated At', | ||||
|     width: 180, | ||||
|     valueFormatter: (p) => p?.value ? new Date(p.value).toLocaleString() : '—' | ||||
|   }, | ||||
|   { field: 'updatedBy', headerName: 'Updated By', width: 160, valueGetter: (p) => p?.row?.updatedBy ?? '—' }, | ||||
| ]; | ||||
|  | ||||
| export default function FurnitureVariantManagement() { | ||||
|   const { user } = useAuth(); | ||||
|   const token = user?.thalosToken || localStorage.getItem('thalosToken'); | ||||
|   const apiRef = useRef(null); | ||||
|  | ||||
|   const [rows, setRows] = useState([]); | ||||
|   const [open, setOpen] = useState(false); | ||||
|   const [editingData, setEditingData] = useState(null); | ||||
|   const [confirmOpen, setConfirmOpen] = useState(false); | ||||
|   const [rowToDelete, setRowToDelete] = useState(null); | ||||
|   const { handleError } = useApiToast(); | ||||
|   const hasLoaded = useRef(false); | ||||
|  | ||||
|   useEffect(() => { | ||||
|     apiRef.current = new FurnitureVariantApi(token); | ||||
|   }, [token]); | ||||
|  | ||||
|   useEffect(() => { | ||||
|     if (!hasLoaded.current) { | ||||
|       loadData(); | ||||
|       hasLoaded.current = true; | ||||
|     } | ||||
|   }, []); | ||||
|  | ||||
|   const loadData = async () => { | ||||
|     try { | ||||
|       const data = await apiRef.current.getAllVariants(); | ||||
|       setRows(Array.isArray(data) ? data : []); | ||||
|     } catch (err) { | ||||
|       console.error('Error loading variants:', err); | ||||
|       handleError(err, 'Failed to load furniture variants'); | ||||
|       setRows([]); | ||||
|     } | ||||
|   }; | ||||
|  | ||||
|   const handleEditClick = (params) => { | ||||
|     if (!params?.row) return; | ||||
|     const r = params.row; | ||||
|     const normalized = { | ||||
|       _id: r._id || r._Id || '', | ||||
|       id: r.id || r.Id || '', | ||||
|       modelId: r.modelId ?? '', | ||||
|       name: r.name ?? '', | ||||
|       color: r.color ?? '', | ||||
|       line: r.line ?? '', | ||||
|       stock: r.stock ?? 0, | ||||
|       price: r.price ?? 0, | ||||
|       currency: r.currency ?? 'USD', | ||||
|       categoryId: r.categoryId ?? '', | ||||
|       providerId: r.providerId ?? '', | ||||
|       attributes: { | ||||
|         material: r?.attributes?.material ?? '', | ||||
|         legs: r?.attributes?.legs ?? '', | ||||
|         origin: r?.attributes?.origin ?? '', | ||||
|       }, | ||||
|       status: r.status ?? 'Active', | ||||
|     }; | ||||
|     setEditingData(normalized); | ||||
|     setOpen(true); | ||||
|   }; | ||||
|  | ||||
|   const handleDeleteClick = (row) => { | ||||
|     setRowToDelete(row); | ||||
|     setConfirmOpen(true); | ||||
|   }; | ||||
|  | ||||
|   const handleConfirmDelete = async () => { | ||||
|     try { | ||||
|       if (!apiRef.current || !rowToDelete?._id) throw new Error('Missing API or id'); | ||||
|       // If your inventory BFF uses soft delete via Update (status=Inactive), do this: | ||||
|       const payload = { | ||||
|         _Id: rowToDelete._id || rowToDelete._Id, | ||||
|         modelId: rowToDelete.modelId, | ||||
|         name: rowToDelete.name, | ||||
|         color: rowToDelete.color, | ||||
|         line: rowToDelete.line, | ||||
|         stock: rowToDelete.stock, | ||||
|         price: rowToDelete.price, | ||||
|         currency: rowToDelete.currency, | ||||
|         categoryId: rowToDelete.categoryId, | ||||
|         providerId: rowToDelete.providerId, | ||||
|         attributes: { | ||||
|           material: rowToDelete?.attributes?.material ?? '', | ||||
|           legs: rowToDelete?.attributes?.legs ?? '', | ||||
|           origin: rowToDelete?.attributes?.origin ?? '', | ||||
|         }, | ||||
|         status: 'Inactive', | ||||
|       }; | ||||
|       // Prefer update soft-delete; if you truly have DELETE, switch to apiRef.current.deleteVariant({ _Id: ... }) | ||||
|       await apiRef.current.updateVariant(payload); | ||||
|       await loadData(); | ||||
|     } catch (e) { | ||||
|       console.error('Delete failed:', e); | ||||
|     } finally { | ||||
|       setConfirmOpen(false); | ||||
|       setRowToDelete(null); | ||||
|     } | ||||
|   }; | ||||
|  | ||||
|   const columns = [ | ||||
|     { | ||||
|       field: 'actions', | ||||
|       headerName: '', | ||||
|       width: 130, | ||||
|       renderCell: (params) => ( | ||||
|         <Box display="flex" alignItems="center" justifyContent="flex-start" height="100%" gap={1}> | ||||
|           <IconButton | ||||
|             size="small" | ||||
|             sx={{ | ||||
|               backgroundColor: '#DFCCBC', | ||||
|               color: '#26201A', | ||||
|               '&:hover': { backgroundColor: '#C2B2A4' }, | ||||
|               borderRadius: 2, | ||||
|               p: 1, | ||||
|             }} | ||||
|             onClick={() => handleEditClick(params)} | ||||
|           > | ||||
|             <EditRoundedIcon fontSize="small" /> | ||||
|           </IconButton> | ||||
|           <IconButton | ||||
|             size="small" | ||||
|             sx={{ | ||||
|               backgroundColor: '#FBE9E7', | ||||
|               color: '#C62828', | ||||
|               '&:hover': { backgroundColor: '#EF9A9A' }, | ||||
|               borderRadius: 2, | ||||
|               p: 1, | ||||
|             }} | ||||
|             onClick={() => handleDeleteClick(params?.row)} | ||||
|           > | ||||
|             <DeleteRoundedIcon fontSize="small" /> | ||||
|           </IconButton> | ||||
|         </Box> | ||||
|       ) | ||||
|     }, | ||||
|     ...columnsBase, | ||||
|   ]; | ||||
|  | ||||
|   return ( | ||||
|     <SectionContainer sx={{ width: '100%' }}> | ||||
|       <Dialog open={open} onClose={() => { setOpen(false); setEditingData(null); }} maxWidth="md" fullWidth> | ||||
|         <DialogTitle>{editingData ? 'Edit Furniture Variant' : 'Add Furniture Variant'}</DialogTitle> | ||||
|         <DialogContent> | ||||
|           <AddOrEditFurnitureVariantForm | ||||
|             initialData={editingData} | ||||
|             onCancel={() => { setOpen(false); setEditingData(null); }} | ||||
|             onAdd={async () => { | ||||
|               await loadData(); | ||||
|               setOpen(false); | ||||
|               setEditingData(null); | ||||
|             }} | ||||
|           /> | ||||
|         </DialogContent> | ||||
|       </Dialog> | ||||
|  | ||||
|       <Dialog open={confirmOpen} onClose={() => setConfirmOpen(false)}> | ||||
|         <DialogTitle>Confirm Delete</DialogTitle> | ||||
|         <DialogContent> | ||||
|           <Typography> | ||||
|             Are you sure you want to delete <strong>{rowToDelete?.name}</strong>? | ||||
|           </Typography> | ||||
|           <Box mt={2} display="flex" justifyContent="flex-end" gap={1}> | ||||
|             <Button onClick={() => setConfirmOpen(false)} className="button-transparent">Cancel</Button> | ||||
|             <Button variant="contained" onClick={handleConfirmDelete} className="button-gold">Delete</Button> | ||||
|           </Box> | ||||
|         </DialogContent> | ||||
|       </Dialog> | ||||
|  | ||||
|       <Box mt={2} sx={{ width: '100%', overflowX: 'auto' }}> | ||||
|         <DataGrid | ||||
|           rows={rows} | ||||
|           columns={columns} | ||||
|           pageSize={5} | ||||
|           rowsPerPageOptions={[5]} | ||||
|           getRowSpacing={() => ({ top: 4, bottom: 4 })} | ||||
|           getRowId={(row) => row._id || row.id || row.modelId} | ||||
|           autoHeight | ||||
|           disableColumnMenu | ||||
|           getRowHeight={() => 'auto'} | ||||
|           sx={{ | ||||
|             '& .MuiDataGrid-cell': { display: 'flex', alignItems: 'center' }, | ||||
|             '& .MuiDataGrid-columnHeader': { display: 'flex', alignItems: 'center' }, | ||||
|           }} | ||||
|         /> | ||||
|         <Box display="flex" justifyContent="flex-end" mt={2}> | ||||
|           <Button variant="contained" className="button-gold" onClick={() => setOpen(true)}> | ||||
|             Add Variant | ||||
|           </Button> | ||||
|         </Box> | ||||
|       </Box> | ||||
|     </SectionContainer> | ||||
|   ); | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 Rodolfo Ruiz
					Rodolfo Ruiz