import * as React from 'react'; import { Box, Typography, Button, IconButton, Link, Tooltip, Card, CardContent, CardActions, Grid, Menu, MenuItem, useMediaQuery, useTheme, Divider, } from '@mui/material'; import { DataGrid, GridColDef, GridActionsCellItem, GridRenderCellParams, } from '@mui/x-data-grid'; import EditIcon from '@mui/icons-material/Edit'; import DeleteIcon from '@mui/icons-material/Delete'; import VisibilityIcon from '@mui/icons-material/Visibility'; import MoreVertIcon from '@mui/icons-material/MoreVert'; import { useNavigate } from 'react-router-dom'; import { ResourceConfig } from '../types/config'; interface EnhancedTableProps { config: ResourceConfig; data: any[]; onEdit: (item: any) => void; onDelete: (id: string) => void; onCreate: () => void; onNavigateToResource?: (resourceName: string, id: string) => void; } export default function EnhancedTable({ config, data, onEdit, onDelete, onCreate, onNavigateToResource, }: EnhancedTableProps) { const theme = useTheme(); const isMobile = useMediaQuery(theme.breakpoints.down('md')); const navigate = useNavigate(); const columns: GridColDef[] = React.useMemo(() => { const cols: GridColDef[] = Object.entries(config.fields).map(([key, field]) => { let muiType: 'string' | 'number' | 'boolean' | 'date' | 'dateTime' | 'singleSelect' = 'string'; if (field.type === 'number') muiType = 'number'; if (field.type === 'boolean') muiType = 'boolean'; if (field.type === 'date') muiType = 'date'; if (field.type === 'datetime') muiType = 'dateTime'; if (field.type === 'enum') muiType = 'singleSelect'; const col: GridColDef = { field: key, headerName: field.label, type: muiType, flex: 1, minWidth: 150, renderCell: (params: GridRenderCellParams) => }; if (muiType === 'date' || muiType === 'dateTime') { col.valueGetter = (value: any) => { if (!value) return null; const date = new Date(value); return isNaN(date.getTime()) ? null : date; }; } if (muiType === 'singleSelect' && field.options) { col.valueOptions = field.options; } return col; }); cols.push({ field: 'actions', type: 'actions', headerName: 'Actions', width: 120, getActions: (params) => [ } label="View" onClick={() => navigate(`/${config.name}/${params.id}`)} />, } label="Edit" onClick={() => navigate(`/${config.name}/edit/${params.id}`)} />, } label="Delete" onClick={() => onDelete(params.id as string)} />, ], }); return cols; }, [config, onDelete, navigate, onNavigateToResource]); if (isMobile) { return ( {config.pluralLabel} {data.map((row) => ( ))} ); } return ( {config.pluralLabel} { const pk = config.primaryKey; if (row[pk] !== undefined && row[pk] !== null) return row[pk]; const fallbackKeys = ['id', '_id', 'uuid', 'pk']; for (const key of fallbackKeys) { if (row[key] !== undefined && row[key] !== null) return row[key]; } return `temp-id-${data.indexOf(row)}`; }} disableRowSelectionOnClick initialState={{ pagination: { paginationModel: { page: 0, pageSize: 10 }, }, }} pageSizeOptions={[10, 25, 50]} sx={{ border: 'none', '& .MuiDataGrid-cell:focus': { outline: 'none' }, '& .MuiDataGrid-columnHeader:focus': { outline: 'none' }, }} /> ); } function MobileCardRow({ row, config, onDelete, onNavigate, navigate }: any) { const [anchorEl, setAnchorEl] = React.useState(null); const open = Boolean(anchorEl); const id = row[config.primaryKey]; const handleClick = (event: React.MouseEvent) => { setAnchorEl(event.currentTarget); }; const handleClose = () => { setAnchorEl(null); }; return ( #{id} { handleClose(); navigate(`/${config.name}/${id}`); }}>View { handleClose(); navigate(`/${config.name}/edit/${id}`); }}>Edit { handleClose(); onDelete(id); }} sx={{ color: 'error.main' }}>Delete {Object.entries(config.fields).slice(0, 5).map(([key, field]: [string, any]) => ( {field.label} ))} ); } function FieldRenderer({ params, field, fieldKey, config, onNavigate, navigate, isMobile }: any) { const value = params.value; const isPk = fieldKey === config.primaryKey; if (field.formatter) return field.formatter(value); if (field.relation && value) { const relationId = typeof value === 'object' ? (value.id || value._id || value.pk) : value; const displayValue = typeof value === "object" ? ((field?.displayField && (value as Record)[field.displayField]) || (value as any).id || (value as any)._id || (value as any).pk) : value; if (relationId) { return ( { e.stopPropagation(); onNavigate?.(field.relation!, String(relationId)); }}> {displayValue} ); } } if (field.type === 'array' && Array.isArray(value)) { const displayList = field.displayField ? value.map((item) => (typeof item === 'object' ? item[field.displayField!] : item)).filter(Boolean).join(', ') : `${value.length} items`; const tooltipTitle = value.map((item) => { if (typeof item !== 'object') return String(item); return item.name || item.title || item.label || item[field.displayField!] || JSON.stringify(item); }).join(', '); return ( {displayList} ); } if (field.type === 'object' && value) { if (field.displayField && value[field.displayField]) return value[field.displayField]; return isMobile ? 'Object' : JSON.stringify(value); } if (field.type === 'boolean') return value ? 'Yes' : 'No'; if (field.type === 'datetime' || field.type === 'date') return value ? new Date(value).toLocaleString() : ''; if (isPk && !isMobile) { return ( { e.stopPropagation(); navigate(`/${config.name}/${params.row[config.primaryKey]}`); }}> {value} ); } return value; }