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',
|
||||
children: [
|
||||
{ title: 'AR Assets Library Management' },
|
||||
{ title: 'Media Management' },
|
||||
] },
|
||||
{
|
||||
title: 'Products',
|
||||
children: [
|
||||
{ title: 'AR Assets Library Management' },
|
||||
{ title: 'Media Management' },
|
||||
]
|
||||
},
|
||||
{ title: 'Product Collections' },
|
||||
]
|
||||
}
|
||||
@@ -54,14 +56,15 @@ const menuData = [
|
||||
title: 'Customers',
|
||||
icon: <PeopleAltIcon />,
|
||||
children: [
|
||||
{ title: 'CRM',
|
||||
{
|
||||
title: 'CRM',
|
||||
children: [
|
||||
{ title: 'Customer List' },
|
||||
{ title: 'Projects' },
|
||||
{ title: 'Customer List' },
|
||||
{ title: 'Projects' },
|
||||
{ title: 'Customer Collections' },
|
||||
]
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
title: 'Sales',
|
||||
children: [
|
||||
@@ -86,12 +89,13 @@ 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>
|
||||
)}
|
||||
@@ -303,4 +309,4 @@ export default function MenuDrawerPrivate({
|
||||
</Tooltip>
|
||||
</Drawer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,64 +1,94 @@
|
||||
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 [category, setCategory] = useState({
|
||||
name: '',
|
||||
description: ''
|
||||
});
|
||||
const { user } = useAuth();
|
||||
const token = user?.thalosToken || localStorage.getItem('thalosToken');
|
||||
const api = useMemo(() => new CategoriesApi(token), [token]);
|
||||
|
||||
useEffect(() => {
|
||||
if (initialData) {
|
||||
setCategory(initialData);
|
||||
} else {
|
||||
setCategory({ name: '', description: '' });
|
||||
}
|
||||
}, [initialData]);
|
||||
const [category, setCategory] = useState({
|
||||
_Id: '',
|
||||
id: '',
|
||||
name: '',
|
||||
description: '',
|
||||
status: 'Active',
|
||||
});
|
||||
|
||||
const handleChange = (e) => {
|
||||
const { name, value } = e.target;
|
||||
setCategory((prev) => ({ ...prev, [name]: value }));
|
||||
};
|
||||
useEffect(() => {
|
||||
if (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({ _Id: '', id: '', name: '', description: '', status: 'Active' });
|
||||
}
|
||||
}, [initialData]);
|
||||
|
||||
const handleSubmit = () => {
|
||||
if (onAdd) {
|
||||
onAdd(category);
|
||||
}
|
||||
};
|
||||
const handleChange = (e) => {
|
||||
const { name, value } = e.target;
|
||||
setCategory((prev) => ({ ...prev, [name]: value }));
|
||||
};
|
||||
|
||||
return (
|
||||
<Box sx={{ px: 2, py: 3 }}>
|
||||
<Paper elevation={0} sx={{ p: 3, bgcolor: '#f9f9f9', borderRadius: 2 }}>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
Category Details
|
||||
</Typography>
|
||||
<TextField
|
||||
fullWidth
|
||||
label="Name"
|
||||
name="name"
|
||||
value={category.name}
|
||||
onChange={handleChange}
|
||||
margin="normal"
|
||||
/>
|
||||
<TextField
|
||||
fullWidth
|
||||
label="Description"
|
||||
name="description"
|
||||
value={category.description}
|
||||
onChange={handleChange}
|
||||
margin="normal"
|
||||
multiline
|
||||
rows={4}
|
||||
/>
|
||||
<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>
|
||||
</Box>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
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 (
|
||||
<Paper sx={{ p: 2 }}>
|
||||
<Typography variant="subtitle1" sx={{ mb: 2 }}>
|
||||
{category._Id ? 'Edit Category' : 'Add Category'}
|
||||
</Typography>
|
||||
|
||||
<TextField
|
||||
name="name"
|
||||
label="Name"
|
||||
value={category.name}
|
||||
onChange={handleChange}
|
||||
fullWidth
|
||||
sx={{ mb: 2 }}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
name="description"
|
||||
label="Description"
|
||||
value={category.description}
|
||||
onChange={handleChange}
|
||||
fullWidth
|
||||
multiline
|
||||
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>
|
||||
</Box>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,142 +1,146 @@
|
||||
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';
|
||||
export default function Categories() {
|
||||
const { user } = useAuth();
|
||||
const token = user?.thalosToken || localStorage.getItem('thalosToken');
|
||||
const api = useMemo(() => new CategoriesApi(token), [token]);
|
||||
|
||||
const columnsBase = [
|
||||
{ field: 'name', headerName: 'Name', flex: 1 },
|
||||
{ field: 'description', headerName: 'Description', flex: 2 }
|
||||
];
|
||||
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);
|
||||
|
||||
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.' }
|
||||
]);
|
||||
useEffect(() => {
|
||||
if (!hasLoaded.current) {
|
||||
loadData();
|
||||
hasLoaded.current = true;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
const [editingCategory, setEditingCategory] = useState(null);
|
||||
const [confirmOpen, setConfirmOpen] = useState(false);
|
||||
const [rowToDelete, setRowToDelete] = useState(null);
|
||||
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 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 }]);
|
||||
}
|
||||
setOpen(false);
|
||||
setEditingCategory(null);
|
||||
};
|
||||
const handleAddClick = () => {
|
||||
setEditingCategory(null);
|
||||
setOpen(true);
|
||||
};
|
||||
|
||||
const handleEditClick = (params) => {
|
||||
setEditingCategory(params.row);
|
||||
setOpen(true);
|
||||
};
|
||||
const handleEditClick = (params) => {
|
||||
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);
|
||||
};
|
||||
|
||||
const handleDeleteClick = (row) => {
|
||||
setRowToDelete(row);
|
||||
setConfirmOpen(true);
|
||||
};
|
||||
const handleDeleteClick = (row) => {
|
||||
setRowToDelete(row);
|
||||
setConfirmOpen(true);
|
||||
};
|
||||
|
||||
const confirmDelete = () => {
|
||||
setRows(rows.filter((row) => row.id !== rowToDelete.id));
|
||||
setRowToDelete(null);
|
||||
setConfirmOpen(false);
|
||||
};
|
||||
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 columns = [
|
||||
...columnsBase,
|
||||
{
|
||||
field: 'actions',
|
||||
headerName: '',
|
||||
width: 130,
|
||||
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>
|
||||
)
|
||||
}
|
||||
];
|
||||
const handleFormDone = async () => {
|
||||
await loadData();
|
||||
setOpen(false);
|
||||
setEditingCategory(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<SectionContainer sx={{ width: '100%' }}>
|
||||
<Typography variant="h4" gutterBottom color='#26201AFF'>
|
||||
Categories
|
||||
</Typography>
|
||||
const columns = [
|
||||
{ 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: 120,
|
||||
sortable: false,
|
||||
filterable: false,
|
||||
renderCell: (params) => (
|
||||
<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>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
<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); }} />
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
return (
|
||||
<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>
|
||||
|
||||
<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={confirmDelete} className="button-gold">Delete</Button>
|
||||
</Box>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
<DataGrid
|
||||
rows={rows}
|
||||
columns={columns}
|
||||
pageSize={10}
|
||||
rowsPerPageOptions={[10]}
|
||||
autoHeight
|
||||
disableColumnMenu
|
||||
getRowId={(r) => r._id || r._Id || r.id || r.Id}
|
||||
/>
|
||||
|
||||
<Box mt={2}>
|
||||
<DataGrid
|
||||
rows={rows}
|
||||
columns={columns}
|
||||
pageSize={5}
|
||||
rowsPerPageOptions={[5]}
|
||||
getRowSpacing={() => ({ top: 8, bottom: 8 })}
|
||||
/>
|
||||
<Dialog open={open} onClose={() => { setOpen(false); setEditingCategory(null); }} fullWidth>
|
||||
<DialogTitle>{editingCategory ? 'Edit Category' : 'Add Category'}</DialogTitle>
|
||||
<DialogContent>
|
||||
<AddOrEditCategoryForm
|
||||
initialData={editingCategory}
|
||||
onAdd={handleFormDone}
|
||||
onCancel={() => { setOpen(false); setEditingCategory(null); }}
|
||||
/>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
<Box display="flex" justifyContent="flex-end" mt={2}>
|
||||
<Button variant="contained" onClick={() => setOpen(true)} className="button-gold">
|
||||
Add Category
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
</SectionContainer>
|
||||
);
|
||||
<Dialog open={confirmOpen} onClose={() => setConfirmOpen(false)}>
|
||||
<DialogTitle>Delete Category</DialogTitle>
|
||||
<DialogContent>
|
||||
<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>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user