feat: added categories
This commit is contained in:
		
							
								
								
									
										
											BIN
										
									
								
								public/logo.png
									
									
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								public/logo.png
									
									
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 3.3 KiB | 
							
								
								
									
										15
									
								
								src/App.jsx
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								src/App.jsx
									
									
									
									
									
								
							| @@ -7,6 +7,7 @@ import Footer from './components/Footer'; | ||||
| import Dashboard from './private/dashboard/Dashboard'; | ||||
| import UserManagement from './private/users/UserManagement'; | ||||
| import FurnitureVariantManagement from './private/fornitures/FurnitureVariantManagement'; | ||||
| import Categories from './private/categories/Categories'; | ||||
| import LoginPage from './private/LoginPage'; | ||||
| import { Routes, Route, Navigate } from 'react-router-dom'; | ||||
| import { useAuth } from './context/AuthContext'; | ||||
| @@ -24,10 +25,8 @@ export default function App() { | ||||
|  | ||||
|   const mainLeft = isMobile ? 0 : (drawerExpanded ? DRAWER_EXPANDED : DRAWER_COLLAPSED); | ||||
|  | ||||
|   // Evita flicker mientras se restaura sesión | ||||
|   if (initializing) return null; | ||||
|  | ||||
|   // === RUTAS PÚBLICAS (sin shell) === | ||||
|   if (!user) { | ||||
|     return ( | ||||
|       <Routes> | ||||
| @@ -37,14 +36,9 @@ export default function App() { | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   // === RUTAS PRIVADAS (con shell) === | ||||
|   return ( | ||||
|     <> | ||||
|       <AppHeader | ||||
|         zone="private" | ||||
|         currentPage={currentView} | ||||
|         leftOffset={mainLeft} | ||||
|       /> | ||||
|       <AppHeader zone="private" currentPage={currentView} leftOffset={mainLeft} /> | ||||
|  | ||||
|       <MenuDrawerPrivate | ||||
|         onSelect={(value) => setCurrentView(value)} | ||||
| @@ -64,9 +58,7 @@ export default function App() { | ||||
|         }} | ||||
|       > | ||||
|         <Routes> | ||||
|           {/* Si ya está autenticado, /login redirige al dashboard */} | ||||
|           <Route path="/login" element={<Navigate to="/" replace />} /> | ||||
|  | ||||
|           <Route | ||||
|             path="/" | ||||
|             element={ | ||||
| @@ -76,6 +68,9 @@ export default function App() { | ||||
|                 {currentView === '/ProductsManagement/CatalogManagement/ProductCollections' && ( | ||||
|                   <FurnitureVariantManagement /> | ||||
|                 )} | ||||
|                 {currentView === '/ProductsManagement/CatalogManagement/Categories' && ( | ||||
|                   <Categories /> | ||||
|                 )} | ||||
|               </> | ||||
|             } | ||||
|           /> | ||||
|   | ||||
							
								
								
									
										54
									
								
								src/api/CategoriesApi.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								src/api/CategoriesApi.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | ||||
| // src/api/CategoriesApi.js | ||||
| export default class CategoriesApi { | ||||
|   constructor(token) { | ||||
|     this.baseUrl = 'https://inventory-bff.dream-views.com/api/v1/Tags'; | ||||
|     this.token = token; | ||||
|   } | ||||
|  | ||||
|   headers(json = true) { | ||||
|     return { | ||||
|       accept: 'application/json', | ||||
|       ...(json ? { 'Content-Type': 'application/json' } : {}), | ||||
|       ...(this.token ? { Authorization: `Bearer ${this.token}` } : {}), | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   async getAll() { | ||||
|     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(); | ||||
|   } | ||||
|  | ||||
|   async create(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 update(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 delete(payload) { | ||||
|     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(); | ||||
|   } | ||||
| } | ||||
| @@ -40,11 +40,13 @@ const menuData = [ | ||||
|                             { title: 'Categories' } | ||||
|                         ] | ||||
|                     }, | ||||
|                     { title: 'Products', | ||||
|                     { | ||||
|                         title: 'Products', | ||||
|                         children: [ | ||||
|                             { title: 'AR  Assets Library Management' }, | ||||
|                             { title: 'Media Management' }, | ||||
|                     ] }, | ||||
|                         ] | ||||
|                     }, | ||||
|                     { title: 'Product Collections' }, | ||||
|                 ] | ||||
|             } | ||||
| @@ -54,7 +56,8 @@ const menuData = [ | ||||
|         title: 'Customers', | ||||
|         icon: <PeopleAltIcon />, | ||||
|         children: [ | ||||
|             { title: 'CRM', | ||||
|             { | ||||
|                 title: 'CRM', | ||||
|                 children: [ | ||||
|                     { title: 'Customer List' }, | ||||
|                     { title: 'Projects' }, | ||||
| @@ -86,7 +89,8 @@ const menuData = [ | ||||
|         icon: <AdminPanelSettingsIcon />, | ||||
|         children: [ | ||||
|             { title: 'Users Management' }, | ||||
|             { title: 'Access Control', | ||||
|             { | ||||
|                 title: 'Access Control', | ||||
|                 children: [ | ||||
|                     { title: 'Roles' }, | ||||
|                     { title: 'Permissions' }, | ||||
| @@ -157,6 +161,8 @@ export default function MenuDrawerPrivate({ | ||||
|                                     onSelect?.('/Users/UserManagement'); | ||||
|                                 } else if (node.title === 'Product Collections') { | ||||
|                                     onSelect?.('/ProductsManagement/CatalogManagement/ProductCollections'); | ||||
|                                 } else if (node.title === 'Categories') { | ||||
|                                     onSelect?.('/ProductsManagement/CatalogManagement/Categories'); | ||||
|                                 } else { | ||||
|                                     onSelect?.(node.title); | ||||
|                                 } | ||||
| @@ -199,7 +205,7 @@ export default function MenuDrawerPrivate({ | ||||
|                 {hasChildren && !collapsed && ( | ||||
|                     <Collapse in={!!openMap[key]} timeout="auto" unmountOnExit> | ||||
|                         <List component="div" disablePadding sx={{ pl: 7 }}> | ||||
|                             {node.children.map((child, idx) => renderNode(child, `${key}-`))} | ||||
|                             {node.children.map((child) => renderNode(child, `${key}-`))} | ||||
|                         </List> | ||||
|                     </Collapse> | ||||
|                 )} | ||||
|   | ||||
| @@ -1,17 +1,32 @@ | ||||
| import { useState, useEffect } from 'react'; | ||||
| import { Box, Button, TextField, Typography, Paper } from '@mui/material'; | ||||
| import { useEffect, useMemo, useState } from 'react'; | ||||
| import { Box, Button, Paper, TextField, Typography } from '@mui/material'; | ||||
| import { useAuth } from '../../context/AuthContext'; | ||||
| import CategoriesApi from '../../api/CategoriesApi'; | ||||
|  | ||||
| export default function AddOrEditCategoryForm({ onAdd, initialData, onCancel }) { | ||||
|   const { user } = useAuth(); | ||||
|   const token = user?.thalosToken || localStorage.getItem('thalosToken'); | ||||
|   const api = useMemo(() => new CategoriesApi(token), [token]); | ||||
|  | ||||
|   const [category, setCategory] = useState({ | ||||
|     _Id: '', | ||||
|     id: '', | ||||
|     name: '', | ||||
|         description: '' | ||||
|     description: '', | ||||
|     status: 'Active', | ||||
|   }); | ||||
|  | ||||
|   useEffect(() => { | ||||
|     if (initialData) { | ||||
|             setCategory(initialData); | ||||
|       setCategory({ | ||||
|         _Id: initialData._id || initialData._Id || '', | ||||
|         id: initialData.id || initialData.Id || initialData._id || initialData._Id || '', | ||||
|         name: initialData.name ?? '', | ||||
|         description: initialData.description ?? '', | ||||
|         status: initialData.status ?? 'Active', | ||||
|       }); | ||||
|     } else { | ||||
|             setCategory({ name: '', description: '' }); | ||||
|       setCategory({ _Id: '', id: '', name: '', description: '', status: 'Active' }); | ||||
|     } | ||||
|   }, [initialData]); | ||||
|  | ||||
| @@ -20,45 +35,60 @@ export default function AddOrEditCategoryForm({ onAdd, initialData, onCancel }) | ||||
|     setCategory((prev) => ({ ...prev, [name]: value })); | ||||
|   }; | ||||
|  | ||||
|     const handleSubmit = () => { | ||||
|         if (onAdd) { | ||||
|             onAdd(category); | ||||
|   const handleSubmit = async () => { | ||||
|     try { | ||||
|       if (category._Id) { | ||||
|         const payload = { | ||||
|           _Id: category._Id, | ||||
|           Id: category.id || category._Id, | ||||
|           name: category.name, | ||||
|           description: category.description, | ||||
|           status: category.status, | ||||
|         }; | ||||
|         await api.update(payload); | ||||
|       } else { | ||||
|         const payload = { | ||||
|           name: category.name, | ||||
|           description: category.description, | ||||
|           status: category.status, | ||||
|         }; | ||||
|         await api.create(payload); | ||||
|       } | ||||
|       onAdd?.(); | ||||
|     } catch (e) { | ||||
|       console.error('Submit category failed:', e); | ||||
|     } | ||||
|   }; | ||||
|  | ||||
|   return ( | ||||
|         <Box sx={{ px: 2, py: 3 }}> | ||||
|             <Paper elevation={0} sx={{ p: 3, bgcolor: '#f9f9f9', borderRadius: 2 }}> | ||||
|                 <Typography variant="h6" gutterBottom> | ||||
|                     Category Details | ||||
|     <Paper sx={{ p: 2 }}> | ||||
|       <Typography variant="subtitle1" sx={{ mb: 2 }}> | ||||
|         {category._Id ? 'Edit Category' : 'Add Category'} | ||||
|       </Typography> | ||||
|  | ||||
|       <TextField | ||||
|                     fullWidth | ||||
|                     label="Name" | ||||
|         name="name" | ||||
|         label="Name" | ||||
|         value={category.name} | ||||
|         onChange={handleChange} | ||||
|                     margin="normal" | ||||
|                 /> | ||||
|                 <TextField | ||||
|         fullWidth | ||||
|                     label="Description" | ||||
|         sx={{ mb: 2 }} | ||||
|       /> | ||||
|  | ||||
|       <TextField | ||||
|         name="description" | ||||
|         label="Description" | ||||
|         value={category.description} | ||||
|         onChange={handleChange} | ||||
|                     margin="normal" | ||||
|         fullWidth | ||||
|         multiline | ||||
|                     rows={4} | ||||
|         minRows={3} | ||||
|       /> | ||||
|  | ||||
|       <Box display="flex" justifyContent="flex-end" gap={1} mt={3}> | ||||
|                     <Button onClick={onCancel} className="button-transparent"> | ||||
|                         Cancel | ||||
|                     </Button> | ||||
|                     <Button variant="contained" onClick={handleSubmit} className="button-gold"> | ||||
|                         Save | ||||
|                     </Button> | ||||
|         <Button onClick={onCancel} className="button-transparent">Cancel</Button> | ||||
|         <Button variant="contained" onClick={handleSubmit} className="button-gold">Save</Button> | ||||
|       </Box> | ||||
|     </Paper> | ||||
|         </Box> | ||||
|   ); | ||||
| } | ||||
|   | ||||
| @@ -1,44 +1,56 @@ | ||||
| import SectionContainer from '../../components/SectionContainer.jsx'; | ||||
| import { useState } from 'react'; | ||||
| import { useEffect, useMemo, useRef, useState } from 'react'; | ||||
| import { Box, Button, Dialog, DialogContent, DialogTitle, IconButton, Typography } from '@mui/material'; | ||||
| import { DataGrid } from '@mui/x-data-grid'; | ||||
| import { Typography, Button, Dialog, DialogTitle, DialogContent, IconButton, Box } from '@mui/material'; | ||||
| import AddOrEditCategoryForm from './AddOrEditCategoryForm.jsx'; | ||||
| import EditIcon from '@mui/icons-material/Edit'; | ||||
| import DeleteIcon from '@mui/icons-material/Delete'; | ||||
| import AddOrEditCategoryForm from './AddOrEditCategoryForm'; | ||||
| import CategoriesApi from '../../api/CategoriesApi'; | ||||
| import { useAuth } from '../../context/AuthContext'; | ||||
|  | ||||
| import EditRoundedIcon from '@mui/icons-material/EditRounded'; | ||||
| import DeleteRoundedIcon from '@mui/icons-material/DeleteRounded'; | ||||
| import '../../App.css'; | ||||
|  | ||||
| const columnsBase = [ | ||||
|     { field: 'name', headerName: 'Name', flex: 1 }, | ||||
|     { field: 'description', headerName: 'Description', flex: 2 } | ||||
| ]; | ||||
|  | ||||
| export default function Categories({ children, maxWidth = 'lg', sx = {} }) { | ||||
|     const [rows, setRows] = useState([ | ||||
|         { id: 1, name: 'Fabrics', description: 'Textile materials including silk, cotton, and synthetics.' }, | ||||
|         { id: 2, name: 'Leather Goods', description: 'Leather-based components for luxury goods.' }, | ||||
|         { id: 3, name: 'Metal Accessories', description: 'Buttons, zippers, and hardware in metal.' }, | ||||
|         { id: 4, name: 'Embellishments', description: 'Decorative materials such as beads and sequins.' } | ||||
|     ]); | ||||
| export default function Categories() { | ||||
|   const { user } = useAuth(); | ||||
|   const token = user?.thalosToken || localStorage.getItem('thalosToken'); | ||||
|   const api = useMemo(() => new CategoriesApi(token), [token]); | ||||
|  | ||||
|   const [rows, setRows] = useState([]); | ||||
|   const [open, setOpen] = useState(false); | ||||
|   const [editingCategory, setEditingCategory] = useState(null); | ||||
|   const [confirmOpen, setConfirmOpen] = useState(false); | ||||
|   const [rowToDelete, setRowToDelete] = useState(null); | ||||
|   const hasLoaded = useRef(false); | ||||
|  | ||||
|     const handleAddOrEditCategory = (category) => { | ||||
|         if (editingCategory) { | ||||
|             setRows(rows.map((row) => (row.id === editingCategory.id ? { ...editingCategory, ...category } : row))); | ||||
|         } else { | ||||
|             const id = rows.length + 1; | ||||
|             setRows([...rows, { id, ...category }]); | ||||
|   useEffect(() => { | ||||
|     if (!hasLoaded.current) { | ||||
|       loadData(); | ||||
|       hasLoaded.current = true; | ||||
|     } | ||||
|         setOpen(false); | ||||
|   }, []); | ||||
|  | ||||
|   const loadData = async () => { | ||||
|     try { | ||||
|       const data = await api.getAll(); | ||||
|       setRows(Array.isArray(data) ? data : []); | ||||
|     } catch (e) { | ||||
|       console.error('Failed to load categories:', e); | ||||
|       setRows([]); | ||||
|     } | ||||
|   }; | ||||
|  | ||||
|   const handleAddClick = () => { | ||||
|     setEditingCategory(null); | ||||
|     setOpen(true); | ||||
|   }; | ||||
|  | ||||
|   const handleEditClick = (params) => { | ||||
|         setEditingCategory(params.row); | ||||
|     const r = params?.row; | ||||
|     if (!r) return; | ||||
|     setEditingCategory({ | ||||
|       _Id: r._id || r._Id || '', | ||||
|       id: r.id || r.Id || '', | ||||
|       name: r.name ?? '', | ||||
|       description: r.description ?? '', | ||||
|       status: r.status ?? 'Active', | ||||
|     }); | ||||
|     setOpen(true); | ||||
|   }; | ||||
|  | ||||
| @@ -47,96 +59,88 @@ export default function Categories({ children, maxWidth = 'lg', sx = {} }) { | ||||
|     setConfirmOpen(true); | ||||
|   }; | ||||
|  | ||||
|     const confirmDelete = () => { | ||||
|         setRows(rows.filter((row) => row.id !== rowToDelete.id)); | ||||
|         setRowToDelete(null); | ||||
|   const confirmDelete = async () => { | ||||
|     try { | ||||
|       if (!rowToDelete) return; | ||||
|       const payload = { | ||||
|         _Id: rowToDelete._id || rowToDelete._Id, | ||||
|         id: rowToDelete.id || rowToDelete.Id || '', | ||||
|         name: rowToDelete.name, | ||||
|         description: rowToDelete.description, | ||||
|         status: 'Inactive', // soft-delete | ||||
|       }; | ||||
|       await api.update(payload); | ||||
|       await loadData(); | ||||
|     } catch (e) { | ||||
|       console.error('Delete failed:', e); | ||||
|     } finally { | ||||
|       setConfirmOpen(false); | ||||
|       setRowToDelete(null); | ||||
|     } | ||||
|   }; | ||||
|  | ||||
|   const handleFormDone = async () => { | ||||
|     await loadData(); | ||||
|     setOpen(false); | ||||
|     setEditingCategory(null); | ||||
|   }; | ||||
|  | ||||
|   const columns = [ | ||||
|         ...columnsBase, | ||||
|     { field: 'name', headerName: 'Name', flex: 1, minWidth: 200 }, | ||||
|     { field: 'description', headerName: 'Description', flex: 1, minWidth: 250 }, | ||||
|     { field: 'status', headerName: 'Status', width: 140, valueGetter: (p) => p.row?.status ?? 'Active' }, | ||||
|     { | ||||
|       field: 'actions', | ||||
|       headerName: '', | ||||
|             width: 130, | ||||
|       width: 120, | ||||
|       sortable: false, | ||||
|       filterable: false, | ||||
|       renderCell: (params) => ( | ||||
|                 <Box display="flex" alignItems="center" justifyContent="flex-end" height="100%" gap={2}> | ||||
|                     <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 sx={{ display: 'flex', gap: 1 }}> | ||||
|           <IconButton size="small" onClick={() => handleEditClick(params)}><EditIcon /></IconButton> | ||||
|           <IconButton size="small" color="error" onClick={() => handleDeleteClick(params.row)}><DeleteIcon /></IconButton> | ||||
|         </Box> | ||||
|             ) | ||||
|         } | ||||
|       ), | ||||
|     }, | ||||
|   ]; | ||||
|  | ||||
|   return ( | ||||
|         <SectionContainer sx={{ width: '100%' }}> | ||||
|             <Typography variant="h4" gutterBottom color='#26201AFF'> | ||||
|                 Categories | ||||
|             </Typography> | ||||
|     <Box> | ||||
|       <Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 2 }}> | ||||
|         <Typography variant="h6">Categories</Typography> | ||||
|         <Button variant="contained" onClick={handleAddClick} className="button-gold">Add Category</Button> | ||||
|       </Box> | ||||
|  | ||||
|       <DataGrid | ||||
|         rows={rows} | ||||
|         columns={columns} | ||||
|         pageSize={10} | ||||
|         rowsPerPageOptions={[10]} | ||||
|         autoHeight | ||||
|         disableColumnMenu | ||||
|         getRowId={(r) => r._id || r._Id || r.id || r.Id} | ||||
|       /> | ||||
|  | ||||
|       <Dialog open={open} onClose={() => { setOpen(false); setEditingCategory(null); }} fullWidth> | ||||
|         <DialogTitle>{editingCategory ? 'Edit Category' : 'Add Category'}</DialogTitle> | ||||
|         <DialogContent> | ||||
|                     <AddOrEditCategoryForm onAdd={handleAddOrEditCategory} initialData={editingCategory} onCancel={() => { setOpen(false); setEditingCategory(null); }} /> | ||||
|           <AddOrEditCategoryForm | ||||
|             initialData={editingCategory} | ||||
|             onAdd={handleFormDone} | ||||
|             onCancel={() => { setOpen(false); setEditingCategory(null); }} | ||||
|           /> | ||||
|         </DialogContent> | ||||
|       </Dialog> | ||||
|  | ||||
|       <Dialog open={confirmOpen} onClose={() => setConfirmOpen(false)}> | ||||
|                 <DialogTitle>Confirm Delete</DialogTitle> | ||||
|         <DialogTitle>Delete Category</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={confirmDelete} className="button-gold">Delete</Button> | ||||
|           <Box sx={{ display: 'flex', gap: 1, mt: 2, mb: 1 }}> | ||||
|             <Button onClick={() => setConfirmOpen(false)}>Cancel</Button> | ||||
|             <Button color="error" variant="contained" onClick={confirmDelete}>Delete</Button> | ||||
|           </Box> | ||||
|         </DialogContent> | ||||
|       </Dialog> | ||||
|  | ||||
|             <Box mt={2}> | ||||
|                 <DataGrid | ||||
|                     rows={rows} | ||||
|                     columns={columns} | ||||
|                     pageSize={5} | ||||
|                     rowsPerPageOptions={[5]} | ||||
|                     getRowSpacing={() => ({ top: 8, bottom: 8 })} | ||||
|                 /> | ||||
|  | ||||
|                 <Box display="flex" justifyContent="flex-end" mt={2}> | ||||
|                     <Button variant="contained" onClick={() => setOpen(true)} className="button-gold"> | ||||
|                         Add Category | ||||
|                     </Button> | ||||
|     </Box> | ||||
|             </Box> | ||||
|         </SectionContainer> | ||||
|   ); | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user