ui improvements for data ttable

This commit is contained in:
2026-04-03 19:29:34 +05:30
parent 7edf3e75da
commit 9b87fb31a7
4 changed files with 96 additions and 31 deletions

View File

@@ -1,10 +1,10 @@
import * as React from 'react';
import { alpha } from '@mui/material/styles';
import {
Box,
Typography,
Button,
IconButton,
Link,
Tooltip,
Card,
CardContent,
@@ -15,6 +15,8 @@ import {
useMediaQuery,
useTheme,
Divider,
Chip,
Stack,
} from '@mui/material';
import {
DataGrid,
@@ -77,6 +79,7 @@ export default function EnhancedTable({
}
if (muiType === 'singleSelect' && field.options) {
// @ts-ignore
col.valueOptions = field.options;
}
@@ -224,58 +227,120 @@ function MobileCardRow({ row, config, onDelete, onNavigate, navigate }: any) {
);
}
function getFormattedDisplayValue(item: any, displayField?: string | string[]) {
if (!item) return "";
if (!displayField) return item.name || item.title || item.label || item.id || JSON.stringify(item);
if (Array.isArray(displayField)) {
return displayField
.map(key => item[key])
.filter(val => val !== undefined && val !== null)
.join(' ');
}
return item[displayField] || item.id || JSON.stringify(item);
}
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) {
// 1. Single Relation
if (field.relation && value && !Array.isArray(value)) {
const relationId = typeof value === 'object' ? (value.id || value._id || value.pk) : value;
const displayValue = typeof value === "object" ?
((field?.displayField && (value as Record<string, any>)[field.displayField]) || (value as any).id || (value as any)._id || (value as any).pk) : value;
const displayValue = getFormattedDisplayValue(value, field.displayField);
if (relationId) {
return (
<Link component="button" variant="body2" sx={{ fontWeight: 'inherit', textAlign: 'left' }} onClick={(e) => { e.stopPropagation(); onNavigate?.(field.relation!, String(relationId)); }}>
{displayValue}
</Link>
);
}
return (
<Chip
label={displayValue}
size="small"
variant="outlined"
color="primary"
onClick={(e) => {
e.stopPropagation();
if (relationId) onNavigate?.(field.relation!, String(relationId));
}}
sx={{ cursor: 'pointer' }}
/>
);
}
// 2. Multi-Select (Array of relations or simple strings)
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(', ');
const tooltipTitle = value.map((item) => getFormattedDisplayValue(item, field.displayField)).join(', ');
return (
<Tooltip title={tooltipTitle} arrow placement="top">
<Box component="span" sx={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', display: 'block' }}>
{displayList}
</Box>
<Stack direction="row" spacing={0.5} sx={{ overflow: 'hidden', flexWrap: 'nowrap' }}>
{value.map((item, idx) => (
<Chip
key={idx}
label={getFormattedDisplayValue(item, field.displayField)}
size="small"
variant="filled"
sx={{ maxWidth: 120 }}
onClick={(e) => {
e.stopPropagation();
if (field.relation) {
const id = typeof item === 'object' ? (item.id || item._id) : item;
if (id) onNavigate?.(field.relation!, String(id));
}
}}
/>
))}
</Stack>
</Tooltip>
);
}
// 3. Simple Objects
if (field.type === 'object' && value) {
if (field.displayField && value[field.displayField]) return value[field.displayField];
return isMobile ? 'Object' : JSON.stringify(value);
return getFormattedDisplayValue(value, field.displayField) || (isMobile ? 'Object' : JSON.stringify(value));
}
if (field.type === 'number' && typeof value === 'number') {
const isNegative = value < 0;
const color = isNegative ? 'error' : 'success';
return (
<Chip
label={value.toLocaleString()}
size="small"
color={color}
variant="filled"
sx={{
fontWeight: 'bold',
minWidth: 60,
// Soft background with bold text for a premium feel
bgcolor: (theme) => alpha(theme.palette[color].main, 0.15),
color: (theme) => theme.palette[color].dark,
'& .MuiChip-label': { px: 1.5 }
}}
/>
);
}
if (field.type === 'boolean') {
return value ? (
<Chip label="Yes" size="small" color="success" variant="outlined" sx={{ height: 20, fontSize: '0.7rem' }} />
) : (
<Chip label="No" size="small" color="default" variant="outlined" sx={{ height: 20, fontSize: '0.7rem' }} />
);
}
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 (
<Link component="button" variant="body2" sx={{ fontWeight: 'inherit' }} onClick={(e) => { e.stopPropagation(); navigate(`/${config.name}/${params.row[config.primaryKey]}`); }}>
{value}
</Link>
<Chip
label={value}
size="small"
color="primary"
onClick={(e) => { e.stopPropagation(); navigate(`/${config.name}/${params.row[config.primaryKey]}`); }}
sx={{ cursor: 'pointer', fontWeight: 'bold' }}
/>
);
}

View File

@@ -14,7 +14,7 @@ export const configuration: Record<string, ResourceOverride> = {
displayField: "name",
},
tags: {
displayField: "icon",
displayField: ["name", "icon"],
},
occurred_at: {
formatter: (val: string) => {

View File

@@ -17,7 +17,7 @@ export interface ResourceField {
options?: string[];
readOnly?: boolean;
schema?: Record<string, ResourceField>;
displayField?: string;
displayField?: string | string[];
formatter?: (value: any) => string;
relation?: string; // Name of the target resource
}

View File

@@ -4,7 +4,7 @@
*/
export interface FieldOverride {
displayField?: string;
displayField?: string | string[];
display?: boolean;
formatter?: (value: any) => string;
}