openapi-spec-reader (#10)
### Summary of Changes:
1. **Spec-Driven Enums**:
- Updated `openapi_loader.ts` to collect all standalone enum schemas (e.g., `FetchRequestStatus`) into the `AppConfig.enums` map.
- Implemented `mergeProperties` and `oneOf`/`anyOf` resolution in `openapi_loader.ts` to ensure complex schemas like `FetchRequest` (using `allOf`) and `source` (using `oneOf`) are correctly parsed.
2. **Customizable Labeling**:
- Added `enumOption` (template-based) and `enumLabels` (mapping-based) to the config and field types.
- Implemented `resolveTemplate` in `utils/options.ts` to handle placeholders like `'{name} {number}'` or plain field names.
3. **UI Integration**:
- **`FormField.tsx`**: Updated relation and enum selects to use `getFieldOptions()` for correct key/value pairs and labels. Added value normalization to extract keys from API objects.
- **`EnhancedTable.tsx`**: Updated `valueOptions` to use key/value pairs for `singleSelect` and updated `FieldRenderer` to show the human-readable label for enums.
- **`FilterBar.tsx`**: Updated `extractOptions` to use spec-driven labels for enum filters.
- **`ResourceView.tsx`**: Centralized filter matching logic into a `getDisplayString` helper, ensuring filter comparisons use the same templates as the UI labels.
4. **App Fixes**:
- `FetchRequests.tsx` and `FetchRequestDetail.tsx` now derive status and format options from the OpenAPI spec via `useConfig()` instead of using hardcoded arrays.
Reviewed-on: #10
Co-authored-by: Vishesh 'ironeagle' Bangotra <aetoskia@gmail.com>
Co-committed-by: Vishesh 'ironeagle' Bangotra <aetoskia@gmail.com>
This commit is contained in:
@@ -31,6 +31,7 @@ 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';
|
||||
import { getFieldOptions, toGridValueOptions, resolveTemplate } from '../utils/options';
|
||||
|
||||
interface EnhancedTableProps {
|
||||
config: ResourceConfig;
|
||||
@@ -95,9 +96,8 @@ export default function EnhancedTable({
|
||||
};
|
||||
}
|
||||
|
||||
if (muiType === 'singleSelect' && field.options) {
|
||||
// @ts-ignore
|
||||
col.valueOptions = field.options;
|
||||
if (muiType === 'singleSelect') {
|
||||
col.valueOptions = toGridValueOptions(getFieldOptions(field));
|
||||
}
|
||||
|
||||
return col;
|
||||
@@ -274,8 +274,9 @@ function MobileCardRow({ row, config, onDelete, onNavigate, navigate }: any) {
|
||||
);
|
||||
}
|
||||
|
||||
function getFormattedDisplayValue(item: any, displayField?: string | string[]) {
|
||||
function getFormattedDisplayValue(item: any, displayField?: string | string[], enumValue?: string) {
|
||||
if (!item) return "";
|
||||
if (enumValue) return resolveTemplate(enumValue, item);
|
||||
if (!displayField) return item.name || item.title || item.label || item.id || JSON.stringify(item);
|
||||
|
||||
if (Array.isArray(displayField)) {
|
||||
@@ -297,7 +298,7 @@ function FieldRenderer({ params, field, fieldKey, config, onNavigate, navigate,
|
||||
// 1. Single Relation
|
||||
if (field.relation && value && !Array.isArray(value)) {
|
||||
const relationId = typeof value === 'object' ? (value.id || value._id || value.pk) : value;
|
||||
const displayValue = getFormattedDisplayValue(value, field.displayField);
|
||||
const displayValue = getFormattedDisplayValue(value, field.displayField, field.enumOption?.value);
|
||||
|
||||
return (
|
||||
<Chip
|
||||
@@ -316,7 +317,8 @@ function FieldRenderer({ params, field, fieldKey, config, onNavigate, navigate,
|
||||
|
||||
// 2. Multi-Select (Array of relations or simple strings)
|
||||
if (field.type === 'array' && Array.isArray(value)) {
|
||||
const tooltipTitle = value.map((item) => getFormattedDisplayValue(item, field.displayField)).join(', ');
|
||||
const enumValue = field.enumOption?.value;
|
||||
const tooltipTitle = value.map((item) => getFormattedDisplayValue(item, field.displayField, enumValue)).join(', ');
|
||||
|
||||
return (
|
||||
<Tooltip title={tooltipTitle} arrow placement="top">
|
||||
@@ -324,7 +326,7 @@ function FieldRenderer({ params, field, fieldKey, config, onNavigate, navigate,
|
||||
{value.map((item, idx) => (
|
||||
<Chip
|
||||
key={idx}
|
||||
label={getFormattedDisplayValue(item, field.displayField)}
|
||||
label={getFormattedDisplayValue(item, field.displayField, enumValue)}
|
||||
size="small"
|
||||
variant="filled"
|
||||
sx={{ maxWidth: 120 }}
|
||||
@@ -344,7 +346,7 @@ function FieldRenderer({ params, field, fieldKey, config, onNavigate, navigate,
|
||||
|
||||
// 3. Simple Objects
|
||||
if (field.type === 'object' && value) {
|
||||
return getFormattedDisplayValue(value, field.displayField) || (isMobile ? 'Object' : JSON.stringify(value));
|
||||
return getFormattedDisplayValue(value, field.displayField, field.enumOption?.value) || (isMobile ? 'Object' : JSON.stringify(value));
|
||||
}
|
||||
|
||||
if (field.type === 'number' && typeof value === 'number') {
|
||||
@@ -379,6 +381,11 @@ function FieldRenderer({ params, field, fieldKey, config, onNavigate, navigate,
|
||||
|
||||
if (field.type === 'datetime' || field.type === 'date') return value ? new Date(value).toLocaleString() : '';
|
||||
|
||||
if (field.type === 'enum') {
|
||||
const opt = getFieldOptions(field).find(o => o.key === value);
|
||||
return opt?.value ?? value;
|
||||
}
|
||||
|
||||
if (isPk && !isMobile) {
|
||||
return (
|
||||
<Chip
|
||||
|
||||
Reference in New Issue
Block a user