import * as React from 'react'; import { Box, Typography, Button, IconButton, Link, Tooltip, } 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 { 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 navigate = useNavigate(); const columns: GridColDef[] = React.useMemo(() => { const cols: GridColDef[] = Object.entries(config.fields).map(([key, field]) => { const col: GridColDef = { field: key, headerName: field.label, flex: 1, minWidth: 150, renderCell: (params: GridRenderCellParams) => { const value = params.value; // 0. Link to View if it's the Primary Key (if it's displayed) const isPk = key === config.primaryKey; // 1. Custom Formatter if (field.formatter) { return field.formatter(value); } // 2. Relational Link 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(); onNavigateToResource?.(field.relation!, String(relationId)); }} > {displayValue} ); } } // 3. Nested Object / Array Display if (field.type === 'array' && Array.isArray(value)) { if (field.displayField) { return value .map((item) => (typeof item === 'object' ? item[field.displayField!] : item)) .filter(Boolean) .join(', '); } return `${value.length} items`; } if (field.type === 'object' && value) { if (field.displayField && value[field.displayField]) { return value[field.displayField]; } return JSON.stringify(value); } // 4. Default renderings if (field.type === 'boolean') return value ? 'Yes' : 'No'; if (field.type === 'datetime' || field.type === 'date') { return (value) ? new Date(value).toLocaleString() : ''; } if (isPk) { return ( { e.stopPropagation(); const rowId = params.row[config.primaryKey]; navigate(`/${config.name}/${rowId}`); }} > {value} ); } return value; } }; 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]); 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={[5, 10, 25]} /> ); }