213 lines
6.4 KiB
JavaScript
213 lines
6.4 KiB
JavaScript
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
import { Box, Button, Dialog, DialogContent, DialogTitle, IconButton, Typography,
|
|
ToggleButton, ToggleButtonGroup } from '@mui/material';
|
|
import { DataGrid } from '@mui/x-data-grid';
|
|
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';
|
|
|
|
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 [statusFilter, setStatusFilter] = useState('Active'); // <- por defecto Active
|
|
const [open, setOpen] = useState(false);
|
|
const [editingCategory, setEditingCategory] = useState(null);
|
|
const [confirmOpen, setConfirmOpen] = useState(false);
|
|
const [rowToDelete, setRowToDelete] = useState(null);
|
|
const hasLoaded = useRef(false);
|
|
|
|
useEffect(() => {
|
|
if (!hasLoaded.current) {
|
|
loadData();
|
|
hasLoaded.current = true;
|
|
}
|
|
}, []);
|
|
|
|
const loadData = async () => {
|
|
try {
|
|
const data = await api.getAll();
|
|
const safeRows = (Array.isArray(data) ? data : [])
|
|
.filter(Boolean)
|
|
.map((r, idx) => ({
|
|
...r,
|
|
__rid: r?._id || r?._Id || r?.id || r?.Id || `tmp-${idx}`,
|
|
}));
|
|
setRows(safeRows);
|
|
} catch (e) {
|
|
console.error('Failed to load categories:', e);
|
|
setRows([]);
|
|
}
|
|
};
|
|
|
|
const handleAddClick = () => {
|
|
setEditingCategory(null);
|
|
setOpen(true);
|
|
};
|
|
|
|
const handleEditClick = (params) => {
|
|
const r = params?.row;
|
|
if (!r) return;
|
|
setEditingCategory({
|
|
_Id: r._id || r._Id || '',
|
|
id: r.id || r.Id || '',
|
|
tagName: r.tagName || r.name || '',
|
|
typeId: r.typeId || '',
|
|
parentTagId: Array.isArray(r.parentTagId) ? r.parentTagId : [],
|
|
slug: r.slug || '',
|
|
displayOrder: Number(r.displayOrder ?? 0),
|
|
icon: r.icon || '',
|
|
status: r.status ?? 'Active',
|
|
});
|
|
setOpen(true);
|
|
};
|
|
|
|
const handleDeleteClick = (row) => {
|
|
if (!row) return;
|
|
setRowToDelete(row);
|
|
setConfirmOpen(true);
|
|
};
|
|
|
|
const pickHexId = (r) =>
|
|
[r?._id, r?._Id, r?.id, r?.Id]
|
|
.filter(Boolean)
|
|
.find((x) => typeof x === 'string' && /^[0-9a-f]{24}$/i.test(x)) || null;
|
|
|
|
const confirmDelete = async () => {
|
|
try {
|
|
if (!rowToDelete) return;
|
|
const hexId = pickHexId(rowToDelete);
|
|
if (!hexId) {
|
|
alert('No se encontró _id (24-hex) para ChangeStatus en esta fila.');
|
|
return;
|
|
}
|
|
await api.changeStatus({ id: hexId, status: 'Inactive' });
|
|
await loadData();
|
|
} catch (e) {
|
|
console.error('Delete failed:', e);
|
|
alert('Delete failed. Revisa la consola para más detalles.');
|
|
} finally {
|
|
setConfirmOpen(false);
|
|
setRowToDelete(null);
|
|
}
|
|
};
|
|
|
|
const handleFormDone = async () => {
|
|
await loadData();
|
|
setOpen(false);
|
|
setEditingCategory(null);
|
|
};
|
|
|
|
// --- FILTRO DE ESTADO ---
|
|
const filteredRows = useMemo(() => {
|
|
if (statusFilter === 'All') return rows;
|
|
const want = String(statusFilter).toLowerCase();
|
|
return rows.filter((r) => String(r?.status ?? 'Active').toLowerCase() === want);
|
|
}, [rows, statusFilter]);
|
|
|
|
const columns = [
|
|
{
|
|
field: 'tagName',
|
|
headerName: 'Name',
|
|
flex: 1,
|
|
minWidth: 200,
|
|
valueGetter: (p) => p?.row?.tagName ?? p?.row?.name ?? '',
|
|
},
|
|
{
|
|
field: 'slug',
|
|
headerName: 'Slug',
|
|
width: 180,
|
|
valueGetter: (p) => p?.row?.slug ?? '',
|
|
},
|
|
{
|
|
field: 'displayOrder',
|
|
headerName: 'Display',
|
|
width: 120,
|
|
valueGetter: (p) => Number(p?.row?.displayOrder ?? 0),
|
|
},
|
|
{
|
|
field: 'status',
|
|
headerName: 'Status',
|
|
width: 140,
|
|
valueGetter: (p) => p?.row?.status ?? 'Active',
|
|
},
|
|
{
|
|
field: 'actions',
|
|
headerName: '',
|
|
width: 120,
|
|
sortable: false,
|
|
filterable: false,
|
|
renderCell: (params) => {
|
|
const r = params?.row;
|
|
if (!r) return null;
|
|
return (
|
|
<Box sx={{ display: 'flex', gap: 1 }}>
|
|
<IconButton size="small" onClick={() => handleEditClick(params)}><EditIcon /></IconButton>
|
|
<IconButton size="small" color="error" onClick={() => handleDeleteClick(r)}><DeleteIcon /></IconButton>
|
|
</Box>
|
|
);
|
|
},
|
|
},
|
|
];
|
|
|
|
return (
|
|
<Box>
|
|
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 2, gap: 2, flexWrap: 'wrap' }}>
|
|
<Typography variant="h6">Categories</Typography>
|
|
|
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
|
|
<ToggleButtonGroup
|
|
value={statusFilter}
|
|
exclusive
|
|
onChange={(_, v) => v && setStatusFilter(v)}
|
|
size="small"
|
|
>
|
|
<ToggleButton value="Active">Active</ToggleButton>
|
|
<ToggleButton value="All">All</ToggleButton>
|
|
<ToggleButton value="Inactive">Inactive</ToggleButton>
|
|
</ToggleButtonGroup>
|
|
|
|
<Button variant="contained" onClick={handleAddClick} className="button-gold">
|
|
Add Category
|
|
</Button>
|
|
</Box>
|
|
</Box>
|
|
|
|
<DataGrid
|
|
rows={filteredRows}
|
|
columns={columns}
|
|
pageSize={10}
|
|
rowsPerPageOptions={[10]}
|
|
autoHeight
|
|
disableColumnMenu
|
|
getRowId={(r) => r?.__rid}
|
|
/>
|
|
|
|
<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>
|
|
|
|
<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>
|
|
);
|
|
}
|