Compare commits
	
		
			6 Commits
		
	
	
		
			98f6f9814d
			...
			0d63977b21
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 0d63977b21 | ||
|   | ee79740d8d | ||
|   | 70afbd4b45 | ||
|   | cef04888c6 | ||
|   | 083b077ebb | ||
|   | b3f939ccdc | 
							
								
								
									
										75
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										75
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -13,6 +13,7 @@ | ||||
|         "@fontsource/roboto": "^5.2.5", | ||||
|         "@mui/icons-material": "^7.1.0", | ||||
|         "@mui/material": "^7.1.0", | ||||
|         "@mui/x-data-grid": "^8.5.0", | ||||
|         "react": "^19.1.0", | ||||
|         "react-dom": "^19.1.0" | ||||
|       }, | ||||
| @@ -1429,6 +1430,65 @@ | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@mui/x-data-grid": { | ||||
|       "version": "8.5.0", | ||||
|       "resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-8.5.0.tgz", | ||||
|       "integrity": "sha512-5rrMm9anFaLk9O5XRIw3J9tAAnaiE1GxFeocyqhDj23RUReMg0YSp3FYnCaFLAehRQVgT9pC4675XO571paxKw==", | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "@babel/runtime": "^7.27.1", | ||||
|         "@mui/utils": "^7.0.2", | ||||
|         "@mui/x-internals": "8.5.0", | ||||
|         "clsx": "^2.1.1", | ||||
|         "prop-types": "^15.8.1", | ||||
|         "reselect": "^5.1.1", | ||||
|         "use-sync-external-store": "^1.5.0" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=14.0.0" | ||||
|       }, | ||||
|       "funding": { | ||||
|         "type": "opencollective", | ||||
|         "url": "https://opencollective.com/mui-org" | ||||
|       }, | ||||
|       "peerDependencies": { | ||||
|         "@emotion/react": "^11.9.0", | ||||
|         "@emotion/styled": "^11.8.1", | ||||
|         "@mui/material": "^5.15.14 || ^6.0.0 || ^7.0.0", | ||||
|         "@mui/system": "^5.15.14 || ^6.0.0 || ^7.0.0", | ||||
|         "react": "^17.0.0 || ^18.0.0 || ^19.0.0", | ||||
|         "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" | ||||
|       }, | ||||
|       "peerDependenciesMeta": { | ||||
|         "@emotion/react": { | ||||
|           "optional": true | ||||
|         }, | ||||
|         "@emotion/styled": { | ||||
|           "optional": true | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@mui/x-internals": { | ||||
|       "version": "8.5.0", | ||||
|       "resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-8.5.0.tgz", | ||||
|       "integrity": "sha512-Ef4KJij1pBGk6/xILyVZHf76tcuRpJIX30k4Ghklsd5QJujZ9ENCGAjvd7aWRAFAs5p3ffn0H8UDESoIcroj1Q==", | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "@babel/runtime": "^7.27.1", | ||||
|         "@mui/utils": "^7.0.2" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=14.0.0" | ||||
|       }, | ||||
|       "funding": { | ||||
|         "type": "opencollective", | ||||
|         "url": "https://opencollective.com/mui-org" | ||||
|       }, | ||||
|       "peerDependencies": { | ||||
|         "@mui/system": "^5.15.14 || ^6.0.0 || ^7.0.0", | ||||
|         "react": "^17.0.0 || ^18.0.0 || ^19.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@popperjs/core": { | ||||
|       "version": "2.11.8", | ||||
|       "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", | ||||
| @@ -3785,6 +3845,12 @@ | ||||
|         "react-dom": ">=16.6.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/reselect": { | ||||
|       "version": "5.1.1", | ||||
|       "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", | ||||
|       "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==", | ||||
|       "license": "MIT" | ||||
|     }, | ||||
|     "node_modules/resolve": { | ||||
|       "version": "1.22.10", | ||||
|       "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", | ||||
| @@ -4239,6 +4305,15 @@ | ||||
|         "punycode": "^2.1.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/use-sync-external-store": { | ||||
|       "version": "1.5.0", | ||||
|       "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz", | ||||
|       "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==", | ||||
|       "license": "MIT", | ||||
|       "peerDependencies": { | ||||
|         "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/vary": { | ||||
|       "version": "1.1.2", | ||||
|       "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", | ||||
|   | ||||
| @@ -15,6 +15,7 @@ | ||||
|     "@fontsource/roboto": "^5.2.5", | ||||
|     "@mui/icons-material": "^7.1.0", | ||||
|     "@mui/material": "^7.1.0", | ||||
|     "@mui/x-data-grid": "^8.5.0", | ||||
|     "react": "^19.1.0", | ||||
|     "react-dom": "^19.1.0" | ||||
|   }, | ||||
|   | ||||
							
								
								
									
										12
									
								
								src/App.jsx
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								src/App.jsx
									
									
									
									
									
								
							| @@ -1,9 +1,12 @@ | ||||
| import { useState } from 'react' | ||||
| import Background from "./components/Background"; | ||||
| import VideoBackground from "./components/VimeoEmbed"; | ||||
| import AppHeader from './components/AppHeader'; | ||||
| import Footer from './components/Footer'; | ||||
| import Box from '@mui/material/Box'; | ||||
|  | ||||
| import Admin from './private/Admin'; | ||||
|  | ||||
| import './App.css' | ||||
|  | ||||
| function App() { | ||||
| @@ -12,7 +15,8 @@ function App() { | ||||
|   return ( | ||||
|     <> | ||||
|  | ||||
|       <Background imageName='background.jpg' opacity={0.65} /> | ||||
|       {/* <Background imageName='background.jpg' opacity={0.65} /> */} | ||||
|       <VideoBackground videoId="1066622045" /> | ||||
|  | ||||
|       <Box | ||||
|         sx={{ | ||||
| @@ -26,9 +30,9 @@ function App() { | ||||
|  | ||||
|         {/* Main content area */} | ||||
|         <Box component="main" sx={{ flex: 1, p: 2 }}> | ||||
|           <h1>Welcome to the Fendi Casa Experience</h1> | ||||
|           <p>This is a sample box.</p> | ||||
|  | ||||
|           {zone === 'private' && <Admin />} | ||||
|           {zone === 'restricted' && <Admin />} | ||||
|           {zone === 'public' && <Admin />} | ||||
|         </Box> | ||||
|         <Footer zone={zone} /> | ||||
|       </Box> | ||||
|   | ||||
| @@ -67,7 +67,7 @@ export default function AppHeader({ zone = 'public' }) { | ||||
|         )} | ||||
|  | ||||
|         {/* Rendering the Drawer */} | ||||
|        <MenuDrawer zone={zone} open={menuOpen} onClose={() => setMenuOpen(false)} /> | ||||
|        <MenuDrawer zone='private' open={menuOpen} onClose={() => setMenuOpen(false)} /> | ||||
|  | ||||
|       </Toolbar> | ||||
|     </AppBar> | ||||
|   | ||||
| @@ -14,7 +14,7 @@ export default function MenuDrawer({ zone = 'public', open, onClose }) { | ||||
|     <Drawer anchor="left" open={open} onClose={onClose} slotProps={{ | ||||
|         paper: { | ||||
|           sx: { | ||||
|             backgroundColor: '#000000a0', // negro semitransparente | ||||
|             backgroundColor: '#000000a0', | ||||
|             width: isMobile ? '100vw' : 250, | ||||
|           }, | ||||
|         }, | ||||
|   | ||||
							
								
								
									
										18
									
								
								src/components/SectionContainer.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/components/SectionContainer.jsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| import { Box } from '@mui/material'; | ||||
|  | ||||
| export default function SectionContainer({ children, maxWidth = 'lg', sx = {} }) { | ||||
|   return ( | ||||
|     <Box | ||||
|       sx={{ | ||||
|         width: '100%', | ||||
|         maxWidth, | ||||
|         mx: 'auto', | ||||
|         px: { xs: 2, md: 4 }, | ||||
|         py: { xs: 3, md: 5 }, | ||||
|         ...sx, | ||||
|       }} | ||||
|     > | ||||
|       {children} | ||||
|     </Box> | ||||
|   ); | ||||
| } | ||||
							
								
								
									
										100
									
								
								src/private/AddOrEditProductForm.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								src/private/AddOrEditProductForm.jsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,100 @@ | ||||
| import React, { useState, useEffect } from 'react'; | ||||
| import { Box, Button, TextField, Grid } from '@mui/material'; | ||||
|  | ||||
| export default function AddOrEditProductForm({ onAdd, initialData, onCancel }) { | ||||
|   const [product, setProduct] = useState({ | ||||
|     name: '', | ||||
|     price: '', | ||||
|     provider: '', | ||||
|     stock: '', | ||||
|     category: '' | ||||
|   }); | ||||
|  | ||||
|   useEffect(() => { | ||||
|     if (initialData) { | ||||
|       setProduct(initialData); | ||||
|     } else { | ||||
|       setProduct({ | ||||
|         name: '', | ||||
|         price: '', | ||||
|         provider: '', | ||||
|         stock: '', | ||||
|         category: '' | ||||
|       }); | ||||
|     } | ||||
|   }, [initialData]); | ||||
|  | ||||
|   const handleChange = (e) => { | ||||
|     const { name, value } = e.target; | ||||
|     setProduct((prev) => ({ | ||||
|       ...prev, | ||||
|       [name]: name === 'price' || name === 'stock' ? Number(value) : value | ||||
|     })); | ||||
|   }; | ||||
|  | ||||
|   const handleSubmit = () => { | ||||
|     if (onAdd) { | ||||
|       onAdd(product); | ||||
|     } | ||||
|   }; | ||||
|  | ||||
|   return ( | ||||
|     <Box mt={2}> | ||||
|       <TextField | ||||
|         fullWidth | ||||
|         label="Name" | ||||
|         name="name" | ||||
|         value={product.name} | ||||
|         onChange={handleChange} | ||||
|         margin="normal" | ||||
|       /> | ||||
|       <TextField | ||||
|         fullWidth | ||||
|         label="Price" | ||||
|         name="price" | ||||
|         type="number" | ||||
|         value={product.price} | ||||
|         onChange={handleChange} | ||||
|         margin="normal" | ||||
|       /> | ||||
|       <Grid container spacing={2}> | ||||
|         <Grid item xs={6}> | ||||
|           <TextField | ||||
|             fullWidth | ||||
|             label="Provider" | ||||
|             name="provider" | ||||
|             value={product.provider} | ||||
|             onChange={handleChange} | ||||
|             margin="normal" | ||||
|           /> | ||||
|         </Grid> | ||||
|         <Grid item xs={6}> | ||||
|           <TextField | ||||
|             fullWidth | ||||
|             label="Stock" | ||||
|             name="stock" | ||||
|             type="number" | ||||
|             value={product.stock} | ||||
|             onChange={handleChange} | ||||
|             margin="normal" | ||||
|           /> | ||||
|         </Grid> | ||||
|       </Grid> | ||||
|       <TextField | ||||
|         fullWidth | ||||
|         label="Category" | ||||
|         name="category" | ||||
|         value={product.category} | ||||
|         onChange={handleChange} | ||||
|         margin="normal" | ||||
|       /> | ||||
|      <Box mt={2}> | ||||
|       {/* Fields... */} | ||||
|       <Box display="flex" justifyContent="flex-end" gap={1} mt={2}> | ||||
|         <Button onClick={onCancel}>Cancel</Button> | ||||
|         <Button variant="contained" onClick={handleSubmit}>Save</Button> | ||||
|       </Box> | ||||
|     </Box> | ||||
|     </Box> | ||||
|   ); | ||||
| } | ||||
							
								
								
									
										126
									
								
								src/private/Admin.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								src/private/Admin.jsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,126 @@ | ||||
|  | ||||
| import SectionContainer from '../components/SectionContainer'; | ||||
| import React, { useState } from 'react'; | ||||
| import { DataGrid } from '@mui/x-data-grid'; | ||||
| import { Typography, Button, Dialog, DialogTitle, DialogContent, IconButton, Box } from '@mui/material'; | ||||
| import AddOrEditProductForm from './AddOrEditProductForm.jsx'; | ||||
| import EditIcon from '@mui/icons-material/Edit'; | ||||
| import DeleteIcon from '@mui/icons-material/Delete'; | ||||
|  | ||||
| const columnsBase = [ | ||||
|     { field: 'id', headerName: 'ID', width: 70 }, | ||||
|     { field: 'company', headerName: 'Company', flex: 1 }, | ||||
|     { field: 'name', headerName: 'Name', flex: 1 }, | ||||
|     { field: 'price', headerName: '$', width: 100, type: 'number' }, | ||||
|     { field: 'provider', headerName: 'Provider', flex: 1 }, | ||||
|     { field: 'stock', headerName: 'Stock', width: 100, type: 'number' }, | ||||
|     { field: 'category', headerName: 'Category', flex: 1 } | ||||
| ]; | ||||
|  | ||||
| export default function Admin({ children, maxWidth = 'lg', sx = {} }) { | ||||
|     const [rows, setRows] = useState([ | ||||
|         { id: 1, company: 'Fendi casa', name: 'Product 1', price: 10.99, provider: 'Provider A', stock: 100, category: 'Home' }, | ||||
|         { id: 2, company: 'Fendi casa', name: 'Product 2', price: 20.0, provider: 'Provider B', stock: 50, category: 'Home' }, | ||||
|         { id: 3, company: 'Fendi casa', name: 'Product 3', price: 5.5, provider: 'Provider C', stock: 200, category: 'Home' }, | ||||
|         { id: 4, company: 'Fendi casa', name: 'Product 4', price: 15.75, provider: 'Provider D', stock: 30, category: 'Home' }, | ||||
|         { id: 5, company: 'Fendi casa', name: 'Product 5', price: 8.2, provider: 'Provider E', stock: 75, category: 'Home' } | ||||
|     ]); | ||||
|  | ||||
|     const [open, setOpen] = useState(false); | ||||
|     const [editingProduct, setEditingProduct] = useState(null); | ||||
|  | ||||
|     const [confirmOpen, setConfirmOpen] = useState(false); | ||||
|     const [rowToDelete, setRowToDelete] = useState(null); | ||||
|  | ||||
|     const handleAddOrEditProduct = (product) => { | ||||
|         if (editingProduct) { | ||||
|             // Update existing | ||||
|             setRows(rows.map((row) => (row.id === editingProduct.id ? { ...editingProduct, ...product } : row))); | ||||
|         } else { | ||||
|             // Add new | ||||
|             const id = rows.length + 1; | ||||
|             setRows([...rows, { id, company: 'Fendi casa', ...product }]); | ||||
|         } | ||||
|         setOpen(false); | ||||
|         setEditingProduct(null); | ||||
|     }; | ||||
|  | ||||
|     const handleEditClick = (params) => { | ||||
|         setEditingProduct(params.row); | ||||
|         setOpen(true); | ||||
|     }; | ||||
|  | ||||
|     const handleDeleteClick = (row) => { | ||||
|         setRowToDelete(row); | ||||
|         setConfirmOpen(true); | ||||
|     }; | ||||
|  | ||||
|     const confirmDelete = () => { | ||||
|         setRows(rows.filter((row) => row.id !== rowToDelete.id)); | ||||
|         setRowToDelete(null); | ||||
|         setConfirmOpen(false); | ||||
|     }; | ||||
|  | ||||
|     const columns = [ | ||||
|         ...columnsBase, | ||||
|         { | ||||
|             field: 'actions', | ||||
|             headerName: 'Actions', | ||||
|             width: 130, | ||||
|             renderCell: (params) => ( | ||||
|                 <Box> | ||||
|                     <IconButton color="primary" onClick={() => handleEditClick(params)}> | ||||
|                         <EditIcon /> | ||||
|                     </IconButton> | ||||
|                     <IconButton color="error" onClick={() => handleDeleteClick(params.row)}> | ||||
|                         <DeleteIcon /> | ||||
|                     </IconButton> | ||||
|                 </Box> | ||||
|             ) | ||||
|         } | ||||
|     ]; | ||||
|  | ||||
|     return ( | ||||
|         <SectionContainer sx={{ width: '100%' }}> | ||||
|             <Typography variant="h6" gutterBottom> | ||||
|                 Product Catalog | ||||
|             </Typography> | ||||
|  | ||||
|             <Dialog open={open} onClose={() => { setOpen(false); setEditingProduct(null); }} maxWidth="sm" fullWidth> | ||||
|                 <DialogTitle>{editingProduct ? 'Edit Product' : 'Add Product'}</DialogTitle> | ||||
|                 <DialogContent> | ||||
|                     <AddOrEditProductForm onAdd={handleAddOrEditProduct} initialData={editingProduct} onCancel={() => { setOpen(false); setEditingProduct(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)}>Cancel</Button> | ||||
|                         <Button variant="contained" color="error" onClick={confirmDelete}>Delete</Button> | ||||
|                     </Box> | ||||
|                 </DialogContent> | ||||
|             </Dialog> | ||||
|  | ||||
|             <Box mt={2}> | ||||
|                 <DataGrid | ||||
|                     rows={rows} | ||||
|                     columns={columns} | ||||
|                     pageSize={5} | ||||
|                     rowsPerPageOptions={[5]} | ||||
|                 /> | ||||
|  | ||||
|                 <Box display="flex" justifyContent="flex-end" mt={2}> | ||||
|                     <Button variant="contained" color="primary" onClick={() => setOpen(true)}> | ||||
|                         Add Product | ||||
|                     </Button> | ||||
|                 </Box> | ||||
|             </Box> | ||||
|         </SectionContainer> | ||||
|     ); | ||||
| } | ||||
		Reference in New Issue
	
	Block a user