dyn options in api
This commit is contained in:
parent
f99b6b7604
commit
b207c0cc5b
1 changed files with 29 additions and 19 deletions
|
|
@ -82,6 +82,8 @@ export interface FormGeneratorFormProps<T = any> {
|
|||
transformField?: (attribute: AttributeDefinition) => AttributeDefinition;
|
||||
// Optional: Custom validation function
|
||||
customValidator?: (formData: T, attributes: AttributeDefinition[]) => Record<string, string>;
|
||||
// Optional: Feature instance ID for feature-scoped options (replaces {instanceId} in API paths)
|
||||
instanceId?: string;
|
||||
}
|
||||
|
||||
// FormGeneratorForm component - Backend-driven form generation
|
||||
|
|
@ -98,9 +100,23 @@ export function FormGeneratorForm<T extends Record<string, any>>({
|
|||
attributes: providedAttributes,
|
||||
filterFields,
|
||||
transformField,
|
||||
customValidator
|
||||
customValidator,
|
||||
instanceId
|
||||
}: FormGeneratorFormProps<T>) {
|
||||
const { t } = useLanguage();
|
||||
|
||||
// Helper to resolve API paths with {instanceId} placeholder
|
||||
const resolveApiPath = (path: string): string => {
|
||||
if (path.includes('{instanceId}')) {
|
||||
const resolvedInstanceId = instanceId || (data as any)?.featureInstanceId;
|
||||
if (!resolvedInstanceId) {
|
||||
console.warn(`API path "${path}" requires instanceId but none provided`);
|
||||
return path;
|
||||
}
|
||||
return path.replace('{instanceId}', resolvedInstanceId);
|
||||
}
|
||||
return path;
|
||||
};
|
||||
const [formData, setFormData] = useState<T>(data || {} as T);
|
||||
const [errors, setErrors] = useState<Record<string, string>>({});
|
||||
const [fieldFocused, setFieldFocused] = useState<Record<string, boolean>>({});
|
||||
|
|
@ -234,7 +250,8 @@ export function FormGeneratorForm<T extends Record<string, any>>({
|
|||
setFieldFocused({});
|
||||
}, [data, getFilteredAttributes]);
|
||||
|
||||
// Fetch options for fields with optionsReference
|
||||
// Fetch options for fields with optionsReference (API path)
|
||||
// Backend provides options in standardized format: { value, label }
|
||||
useEffect(() => {
|
||||
const fetchOptions = async () => {
|
||||
const filteredAttrs = getFilteredAttributes();
|
||||
|
|
@ -250,39 +267,32 @@ export function FormGeneratorForm<T extends Record<string, any>>({
|
|||
for (const field of fieldsToFetch) {
|
||||
if (typeof field.options !== 'string') continue;
|
||||
|
||||
const optionKey = field.options;
|
||||
setLoadingOptions(prev => ({ ...prev, [field.name]: true }));
|
||||
|
||||
try {
|
||||
const response = await api.get(`/api/options/${field.options}`);
|
||||
// Backend provides full API path (e.g., "/api/connections/statuses/options")
|
||||
// Resolve {instanceId} placeholder if present
|
||||
const apiPath = resolveApiPath(optionKey);
|
||||
const response = await api.get(apiPath);
|
||||
|
||||
let fetchedOptions: Array<{ value: string | number; label: string }> = [];
|
||||
|
||||
if (Array.isArray(response.data)) {
|
||||
// Backend returns standardized format: [{ value, label }]
|
||||
fetchedOptions = response.data.map((opt: any) => {
|
||||
if (typeof opt === 'string' || typeof opt === 'number') {
|
||||
return { value: opt, label: String(opt) };
|
||||
}
|
||||
// Handle multilingual labels
|
||||
const labelValue = typeof opt.label === 'string'
|
||||
? opt.label
|
||||
: opt.label?.en || opt.label?.[Object.keys(opt.label || {})[0]] || String(opt.value);
|
||||
return {
|
||||
value: opt.value,
|
||||
label: labelValue
|
||||
};
|
||||
});
|
||||
} else if (response.data?.options && Array.isArray(response.data.options)) {
|
||||
fetchedOptions = response.data.options.map((opt: any) => {
|
||||
const labelValue = typeof opt.label === 'string'
|
||||
? opt.label
|
||||
: opt.label?.en || opt.label?.[Object.keys(opt.label || {})[0]] || String(opt.value);
|
||||
return {
|
||||
value: opt.value,
|
||||
label: labelValue
|
||||
};
|
||||
return { value: opt.value, label: labelValue };
|
||||
});
|
||||
}
|
||||
|
||||
setOptionsCache(prev => ({ ...prev, [field.options as string]: fetchedOptions }));
|
||||
setOptionsCache(prev => ({ ...prev, [optionKey]: fetchedOptions }));
|
||||
} catch (error: any) {
|
||||
console.error(`Failed to fetch options for ${field.options}:`, error);
|
||||
setOptionsCache(prev => ({ ...prev, [field.options as string]: [] }));
|
||||
|
|
@ -293,7 +303,7 @@ export function FormGeneratorForm<T extends Record<string, any>>({
|
|||
};
|
||||
|
||||
fetchOptions();
|
||||
}, [getFilteredAttributes, optionsCache]);
|
||||
}, [getFilteredAttributes, optionsCache, resolveApiPath]);
|
||||
|
||||
// Handle field focus
|
||||
const handleFieldFocus = (fieldName: string, focused: boolean) => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue