diff --git a/src_generic/components/EnhancedTable.tsx b/src_generic/components/EnhancedTable.tsx index e3afc14..8ee8207 100644 --- a/src_generic/components/EnhancedTable.tsx +++ b/src_generic/components/EnhancedTable.tsx @@ -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)[field.displayField]) || (value as any).id || (value as any)._id || (value as any).pk) : value; + const displayValue = getFormattedDisplayValue(value, field.displayField); - if (relationId) { - return ( - { e.stopPropagation(); onNavigate?.(field.relation!, String(relationId)); }}> - {displayValue} - - ); - } + return ( + { + 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 ( - - {displayList} - + + {value.map((item, idx) => ( + { + e.stopPropagation(); + if (field.relation) { + const id = typeof item === 'object' ? (item.id || item._id) : item; + if (id) onNavigate?.(field.relation!, String(id)); + } + }} + /> + ))} + ); } + // 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 === 'boolean') return value ? 'Yes' : 'No'; + if (field.type === 'number' && typeof value === 'number') { + const isNegative = value < 0; + const color = isNegative ? 'error' : 'success'; + + return ( + alpha(theme.palette[color].main, 0.15), + color: (theme) => theme.palette[color].dark, + '& .MuiChip-label': { px: 1.5 } + }} + /> + ); + } + + if (field.type === 'boolean') { + return value ? ( + + ) : ( + + ); + } + 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} - + { e.stopPropagation(); navigate(`/${config.name}/${params.row[config.primaryKey]}`); }} + sx={{ cursor: 'pointer', fontWeight: 'bold' }} + /> ); } diff --git a/src_generic/configuration.ts b/src_generic/configuration.ts index 4989f6f..41d0662 100644 --- a/src_generic/configuration.ts +++ b/src_generic/configuration.ts @@ -14,7 +14,7 @@ export const configuration: Record = { displayField: "name", }, tags: { - displayField: "icon", + displayField: ["name", "icon"], }, occurred_at: { formatter: (val: string) => { diff --git a/src_generic/types/config.ts b/src_generic/types/config.ts index ded62d9..78d6989 100644 --- a/src_generic/types/config.ts +++ b/src_generic/types/config.ts @@ -17,7 +17,7 @@ export interface ResourceField { options?: string[]; readOnly?: boolean; schema?: Record; - displayField?: string; + displayField?: string | string[]; formatter?: (value: any) => string; relation?: string; // Name of the target resource } diff --git a/src_generic/types/overrides.ts b/src_generic/types/overrides.ts index 00cb060..31cfe04 100644 --- a/src_generic/types/overrides.ts +++ b/src_generic/types/overrides.ts @@ -4,7 +4,7 @@ */ export interface FieldOverride { - displayField?: string; + displayField?: string | string[]; display?: boolean; formatter?: (value: any) => string; }