Files
fendi-react-app/src/private/categories/Categories.jsx

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>
);
}