features: services and filters implemented
This commit is contained in:
		| @@ -1,5 +1,6 @@ | ||||
| import { useEffect, useMemo, useRef, useState } from 'react'; | ||||
| import { Box, Button, Dialog, DialogContent, DialogTitle, IconButton, Typography } from '@mui/material'; | ||||
| 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'; | ||||
| @@ -13,6 +14,7 @@ export default function Categories() { | ||||
|   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); | ||||
| @@ -29,7 +31,13 @@ export default function Categories() { | ||||
|   const loadData = async () => { | ||||
|     try { | ||||
|       const data = await api.getAll(); | ||||
|       setRows(Array.isArray(data) ? data : []); | ||||
|       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([]); | ||||
| @@ -47,32 +55,41 @@ export default function Categories() { | ||||
|     setEditingCategory({ | ||||
|       _Id: r._id || r._Id || '', | ||||
|       id: r.id || r.Id || '', | ||||
|       name: r.tagName ?? r.name ?? '', | ||||
|       slug: r.slug ?? '', | ||||
|       typeId: r.typeId ?? '', | ||||
|       tagName: r.tagName || r.name || '', | ||||
|       typeId: r.typeId || '', | ||||
|       parentTagId: Array.isArray(r.parentTagId) ? r.parentTagId : [], | ||||
|       displayOrder: Number.isFinite(r.displayOrder) ? r.displayOrder : 0, | ||||
|       icon: r.icon ?? '', | ||||
|       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; | ||||
|       await api.changeStatus({ | ||||
|         id: rowToDelete.id || rowToDelete.Id || rowToDelete._id || rowToDelete._Id, | ||||
|         status: 'Inactive', | ||||
|       }); | ||||
|       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); | ||||
| @@ -85,41 +102,89 @@ export default function Categories() { | ||||
|     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: 220 }, | ||||
|     { field: 'displayOrder', headerName: 'Display', width: 120, valueGetter: (p) => p.row?.displayOrder ?? 0 }, | ||||
|     { field: 'status', headerName: 'Status', width: 140, valueGetter: (p) => p.row?.status ?? 'Active' }, | ||||
|     { | ||||
|       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) => ( | ||||
|         <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> | ||||
|       ), | ||||
|       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 }}> | ||||
|       <Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 2, gap: 2, flexWrap: 'wrap' }}> | ||||
|         <Typography variant="h6">Categories</Typography> | ||||
|         <Button variant="contained" onClick={handleAddClick} className="button-gold">Add Category</Button> | ||||
|  | ||||
|         <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={rows} | ||||
|         rows={filteredRows} | ||||
|         columns={columns} | ||||
|         pageSize={10} | ||||
|         rowsPerPageOptions={[10]} | ||||
|         autoHeight | ||||
|         disableColumnMenu | ||||
|         getRowId={(r) => r._id || r._Id || r.id || r.Id} | ||||
|         getRowId={(r) => r?.__rid} | ||||
|       /> | ||||
|  | ||||
|       <Dialog open={open} onClose={() => { setOpen(false); setEditingCategory(null); }} fullWidth> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user