From b207c0cc5bf553d1264f182a67244b314e0c88e4 Mon Sep 17 00:00:00 2001
From: ValueOn AG
Date: Thu, 22 Jan 2026 18:52:07 +0100
Subject: [PATCH] dyn options in api
---
.../FormGeneratorForm/FormGeneratorForm.tsx | 48 +++++++++++--------
1 file changed, 29 insertions(+), 19 deletions(-)
diff --git a/src/components/FormGenerator/FormGeneratorForm/FormGeneratorForm.tsx b/src/components/FormGenerator/FormGeneratorForm/FormGeneratorForm.tsx
index 2e9e7a7..640f143 100644
--- a/src/components/FormGenerator/FormGeneratorForm/FormGeneratorForm.tsx
+++ b/src/components/FormGenerator/FormGeneratorForm/FormGeneratorForm.tsx
@@ -82,6 +82,8 @@ export interface FormGeneratorFormProps {
transformField?: (attribute: AttributeDefinition) => AttributeDefinition;
// Optional: Custom validation function
customValidator?: (formData: T, attributes: AttributeDefinition[]) => Record;
+ // 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>({
attributes: providedAttributes,
filterFields,
transformField,
- customValidator
+ customValidator,
+ instanceId
}: FormGeneratorFormProps) {
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(data || {} as T);
const [errors, setErrors] = useState>({});
const [fieldFocused, setFieldFocused] = useState>({});
@@ -234,7 +250,8 @@ export function FormGeneratorForm>({
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>({
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>({
};
fetchOptions();
- }, [getFilteredAttributes, optionsCache]);
+ }, [getFilteredAttributes, optionsCache, resolveApiPath]);
// Handle field focus
const handleFieldFocus = (fieldName: string, focused: boolean) => {