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