From dbec57d647ddecc7c5a7a701083423d55e46c871 Mon Sep 17 00:00:00 2001
From: ValueOn AG
Date: Fri, 17 Apr 2026 12:30:50 +0200
Subject: [PATCH] udb fixed toggles
---
src/components/UnifiedDataBar/SourcesTab.tsx | 518 ++++++++-----------
1 file changed, 229 insertions(+), 289 deletions(-)
diff --git a/src/components/UnifiedDataBar/SourcesTab.tsx b/src/components/UnifiedDataBar/SourcesTab.tsx
index 7580ec0..aec1197 100644
--- a/src/components/UnifiedDataBar/SourcesTab.tsx
+++ b/src/components/UnifiedDataBar/SourcesTab.tsx
@@ -683,14 +683,14 @@ const SourcesTab: React.FC = ({ context, onSourcesChanged, onSe
try {
const res = await api.get(`/api/workspace/${instanceId}/feature-connections/${node.featureInstanceId}/tables`);
const tables: FeatureTableNode[] = (res.data.tables || []).map((t: any) => ({
- objectKey: t.objectKey,
- tableName: t.tableName,
- label: t.label || {},
- fields: t.fields || [],
- isParent: t.isParent || false,
- parentTable: t.parentTable || undefined,
- parentKey: t.parentKey || undefined,
- displayFields: t.displayFields || undefined,
+ objectKey: t.objectKey ?? '',
+ tableName: t.tableName ?? '',
+ label: t.label ?? '',
+ fields: t.fields ?? [],
+ isParent: Boolean(t.isParent),
+ parentTable: t.parentTable ?? null,
+ parentKey: t.parentKey ?? null,
+ displayFields: t.displayFields ?? [],
}));
if (mountedRef.current) {
setFeatureTree(prev => _mapFeatureTreeUpdate(prev, node.featureInstanceId, n => ({
@@ -707,11 +707,11 @@ const SourcesTab: React.FC = ({ context, onSourcesChanged, onSe
}, [instanceId]);
/* ── Feature: Add table as FeatureDataSource ── */
- const _addFeatureTable = useCallback(async (node: FeatureConnectionNode, table: FeatureTableNode) => {
+ const _addFeatureTable = useCallback(async (node: FeatureConnectionNode, table: FeatureTableNode): Promise => {
const key = `${node.featureInstanceId}-${table.tableName}`;
setAddingFeatureKey(key);
try {
- await api.post(`/api/workspace/${instanceId}/feature-datasources`, {
+ const res = await api.post(`/api/workspace/${instanceId}/feature-datasources`, {
featureInstanceId: node.featureInstanceId,
featureCode: node.featureCode,
tableName: table.tableName,
@@ -720,8 +720,10 @@ const SourcesTab: React.FC = ({ context, onSourcesChanged, onSe
});
_fetchFeatureDataSources();
onSourcesChanged?.();
+ return res.data?.id || null;
} catch (err) {
console.error('Failed to add feature data source:', err);
+ return null;
} finally {
if (mountedRef.current) setAddingFeatureKey(null);
}
@@ -895,7 +897,7 @@ const SourcesTab: React.FC = ({ context, onSourcesChanged, onSe
node={node}
depth={0}
onToggle={_toggleNode}
- onAdd={_addAsDataSource}
+ onEnsureDs={_addAsDataSource}
isAdded={_isAdded}
addingPath={addingPath}
dataSources={dataSources}
@@ -945,7 +947,7 @@ const SourcesTab: React.FC = ({ context, onSourcesChanged, onSe
group={g}
onToggleGroup={_toggleMandateGroup}
onToggleFeature={_toggleFeatureNode}
- onAddTable={_addFeatureTable}
+ onEnsureFds={_addFeatureTable}
isTableAdded={_isFeatureTableAdded}
addingKey={addingFeatureKey}
onToggleParentGroup={_toggleParentGroup}
@@ -983,7 +985,7 @@ interface _TreeNodeViewProps {
node: TreeNode;
depth: number;
onToggle: (node: TreeNode) => void;
- onAdd: (node: TreeNode) => void;
+ onEnsureDs: (node: TreeNode) => Promise;
isAdded: (connectionId: string, service: string | undefined, path: string | undefined) => boolean;
addingPath: string | null;
dataSources: UdbDataSource[];
@@ -999,7 +1001,7 @@ interface _TreeNodeViewProps {
}
const _TreeNodeView: React.FC<_TreeNodeViewProps> = ({
- node, depth, onToggle, onAdd, isAdded, addingPath,
+ node, depth, onToggle, onEnsureDs, isAdded, addingPath,
dataSources, onCycleScope, onToggleNeutralize, onRemoveDs, onSendToChat, scopeCycleTitle,
selectedKeys, onSelect, inheritedScope, inheritedNeutralize,
}) => {
@@ -1010,8 +1012,6 @@ const _TreeNodeView: React.FC<_TreeNodeViewProps> = ({
? (node.expanded ? '\u25BE' : '\u25B8')
: '\u00A0\u00A0';
const ds = _findDs(dataSources, node);
- const alreadyAdded = isAdded(node.connectionId, node.service, node.path);
- const isAdding = addingPath === node.key;
const effectiveScope = ds?.scope ?? inheritedScope;
const effectiveNeutralize = ds?.neutralize ?? inheritedNeutralize ?? false;
@@ -1111,45 +1111,41 @@ const _TreeNodeView: React.FC<_TreeNodeViewProps> = ({
{'\u{1F4AC}'}
- {/* Scope: own DS → clickable, inherited → dimmed static */}
- {ds ? (
-
- ) : (
-
- {_SCOPE_ICONS[effectiveScope || 'personal']}
-
- )}
+ {/* Scope: own DS → cycle, no DS → create DS then cycle */}
+
- {/* Neutralize: own DS → clickable, inherited → dimmed static */}
- {ds ? (
-
- ) : (
-
- {'\uD83D\uDD12'}
-
- )}
+ {/* Neutralize: own DS → toggle, no DS → create DS then toggle */}
+
{/* Remove: only when DS exists */}
{ds && (
@@ -1162,23 +1158,6 @@ const _TreeNodeView: React.FC<_TreeNodeViewProps> = ({
)}
- {/* Add button: on hover when not yet added */}
- {hovered && !alreadyAdded && !ds && (
-
- )}
{node.expanded && node.children && node.children.length > 0 && (
@@ -1189,7 +1168,7 @@ const _TreeNodeView: React.FC<_TreeNodeViewProps> = ({
node={child}
depth={depth + 1}
onToggle={onToggle}
- onAdd={onAdd}
+ onEnsureDs={onEnsureDs}
isAdded={isAdded}
addingPath={addingPath}
dataSources={dataSources}
@@ -1231,7 +1210,7 @@ interface _MandateGroupViewProps extends _FdsActionProps {
group: MandateGroupNode;
onToggleGroup: (mandateId: string) => void;
onToggleFeature: (node: FeatureConnectionNode) => void;
- onAddTable: (node: FeatureConnectionNode, table: FeatureTableNode) => void;
+ onEnsureFds: (node: FeatureConnectionNode, table: FeatureTableNode) => Promise;
isTableAdded: (featureInstanceId: string, tableName: string) => boolean;
addingKey: string | null;
onToggleParentGroup: (node: FeatureConnectionNode, parentTableName: string) => void;
@@ -1245,7 +1224,7 @@ interface _MandateGroupViewProps extends _FdsActionProps {
}
const _MandateGroupView: React.FC<_MandateGroupViewProps> = ({
- group, onToggleGroup, onToggleFeature, onAddTable, isTableAdded, addingKey,
+ group, onToggleGroup, onToggleFeature, onEnsureFds, isTableAdded, addingKey,
onToggleParentGroup, onToggleParentRecord, onAddParentRecord, isParentRecordAdded,
expandedParentGroups, loadingParentGroup, addingParentKey, onSendToChat,
featureDataSources, onCycleScope, onToggleNeutralize, onToggleNeutralizeField,
@@ -1283,7 +1262,7 @@ const _MandateGroupView: React.FC<_MandateGroupViewProps> = ({
key={fNode.featureInstanceId}
node={fNode}
onToggle={onToggleFeature}
- onAddTable={onAddTable}
+ onEnsureFds={onEnsureFds}
isTableAdded={isTableAdded}
addingKey={addingKey}
onToggleParentGroup={onToggleParentGroup}
@@ -1313,7 +1292,7 @@ const _MandateGroupView: React.FC<_MandateGroupViewProps> = ({
interface _FeatureNodeViewProps extends _FdsActionProps {
node: FeatureConnectionNode;
onToggle: (node: FeatureConnectionNode) => void;
- onAddTable: (node: FeatureConnectionNode, table: FeatureTableNode) => void;
+ onEnsureFds: (node: FeatureConnectionNode, table: FeatureTableNode) => Promise;
isTableAdded: (featureInstanceId: string, tableName: string) => boolean;
addingKey: string | null;
onToggleParentGroup: (node: FeatureConnectionNode, parentTableName: string) => void;
@@ -1327,7 +1306,7 @@ interface _FeatureNodeViewProps extends _FdsActionProps {
}
const _FeatureNodeView: React.FC<_FeatureNodeViewProps> = ({
- node, onToggle, onAddTable, isTableAdded, addingKey,
+ node, onToggle, onEnsureFds,
onToggleParentGroup, onToggleParentRecord, onAddParentRecord, isParentRecordAdded,
expandedParentGroups, loadingParentGroup, addingParentKey, onSendToChat,
featureDataSources, onCycleScope, onToggleNeutralize, onToggleNeutralizeField,
@@ -1387,46 +1366,60 @@ const _FeatureNodeView: React.FC<_FeatureNodeViewProps> = ({
{node.tableCount} {t('Tabellen')}
- {(wildcardFds || hovered) && (
-
- )}
+ {/* Chat: always visible */}
+
- {wildcardFds && (
-
- )}
- {wildcardFds && (
-
- )}
+ {/* Scope: own wildcard-FDS → cycle, otherwise create then cycle */}
+
+
+ {/* Neutralize: own wildcard-FDS → toggle, otherwise create then toggle */}
+
+
+ {/* Remove: only when wildcard-FDS exists */}
{wildcardFds && (
)}
- {!wildcardFds && hovered && (
-
- )}
{node.expanded && node.tables && node.tables.length > 0 && (
@@ -1511,9 +1482,7 @@ const _FeatureNodeView: React.FC<_FeatureNodeViewProps> = ({
key={table.objectKey}
featureNode={node}
table={table}
- onAdd={onAddTable}
- isAdded={isTableAdded(node.featureInstanceId, table.tableName)}
- isAdding={addingKey === `${node.featureInstanceId}-${table.tableName}`}
+ onEnsureFds={onEnsureFds}
onSendToChat={onSendToChat}
fds={fds}
onCycleScope={onCycleScope}
@@ -1543,9 +1512,7 @@ const _FeatureNodeView: React.FC<_FeatureNodeViewProps> = ({
interface _FeatureTableRowProps {
featureNode: FeatureConnectionNode;
table: FeatureTableNode;
- onAdd: (node: FeatureConnectionNode, table: FeatureTableNode) => void;
- isAdded: boolean;
- isAdding: boolean;
+ onEnsureFds: (node: FeatureConnectionNode, table: FeatureTableNode) => Promise;
onSendToChat?: (params: { featureInstanceId: string; featureCode: string; tableName?: string; objectKey: string; label: string; fieldName?: string }) => void;
fds?: UdbFeatureDataSource;
onCycleScope?: (fds: UdbFeatureDataSource) => void;
@@ -1558,7 +1525,7 @@ interface _FeatureTableRowProps {
}
const _FeatureTableRow: React.FC<_FeatureTableRowProps> = ({
- featureNode, table, onAdd, isAdded, isAdding, onSendToChat,
+ featureNode, table, onEnsureFds, onSendToChat,
fds, onCycleScope, onToggleNeutralize, onToggleNeutralizeField,
onRemoveFds, featureTree, inheritedScope, inheritedNeutralize,
}) => {
@@ -1620,38 +1587,56 @@ const _FeatureTableRow: React.FC<_FeatureTableRowProps> = ({
)}
- {(fds || hovered) && (
-
- )}
+ {/* Chat: always visible */}
+
- {fds && onCycleScope && (
-
- )}
- {fds && onToggleNeutralize && (
-
- )}
+ {/* Scope: own FDS → cycle, otherwise create then cycle */}
+
+
+ {/* Neutralize: own FDS → toggle, otherwise create then toggle */}
+
+
+ {/* Remove: only when FDS exists */}
{fds && onRemoveFds && (
)}
- {/* Inherited scope/neutralize indicators (no own FDS) */}
- {!fds && effectiveScope && (
-
- {_SCOPE_ICONS[effectiveScope] || _SCOPE_ICONS.personal}
-
- )}
- {!fds && effectiveNeutralize && (
-
- {'\uD83D\uDD12'}
-
- )}
-
- {!fds && hovered && !isAdded && (
-
- )}
{/* Expandable field sub-nodes */}
@@ -1780,21 +1732,21 @@ const _FeatureFieldRow: React.FC<_FeatureFieldRowProps> = ({
{fieldName}
- {(fds || hovered) && (
-
- )}
+ {/* Chat: always visible */}
+
- {fds && onToggleNeutralizeField && (
+ {/* Neutralize: own FDS → clickable, otherwise dimmed */}
+ {fds && onToggleNeutralizeField ? (
- )}
-
- {inheritedScope && (
+ ) : (
- {_SCOPE_ICONS[inheritedScope] || _SCOPE_ICONS.personal}
+ {'\uD83D\uDD12'}
)}
+
+ {/* Scope: inherited indicator */}
+
+ {_SCOPE_ICONS[inheritedScope || 'personal']}
+
);
};
@@ -1942,7 +1900,7 @@ interface _ParentRecordRowProps {
const _ParentRecordRow: React.FC<_ParentRecordRowProps> = ({
featureNode, record, childTables, allTables: _allTables,
- onToggle, onAdd, isAdded, isAdding,
+ onToggle,
onSendToChat, fds, onCycleScope, onToggleNeutralize, onRemoveFds,
inheritedScope, inheritedNeutralize,
}) => {
@@ -1991,31 +1949,29 @@ const _ParentRecordRow: React.FC<_ParentRecordRowProps> = ({
{record.displayLabel}
- {/* Chat-Senden: always visible when fds, hover-only otherwise */}
- {(fds || hovered) && (
-
- )}
+ {/* Chat: always visible */}
+
- {/* FDS inline actions */}
- {fds && onCycleScope && (
+ {/* Scope: own FDS → clickable, otherwise dimmed */}
+ {fds && onCycleScope ? (
+ ) : (
+
+ {_SCOPE_ICONS[inheritedScope || 'personal']}
+
)}
- {fds && onToggleNeutralize && (
+
+ {/* Neutralize: own FDS → clickable, otherwise dimmed */}
+ {fds && onToggleNeutralize ? (
+ ) : (
+
+ {'\uD83D\uDD12'}
+
)}
+
+ {/* Remove: only when FDS exists */}
{fds && onRemoveFds && (
)}
- {/* Inherited scope/neutralize indicators */}
- {!fds && inheritedScope && (
-
- {_SCOPE_ICONS[inheritedScope] || _SCOPE_ICONS.personal}
-
- )}
- {!fds && (inheritedNeutralize ?? false) && (
-
- {'\uD83D\uDD12'}
-
- )}
-
- {/* Add button (only when not yet added) */}
- {!fds && hovered && !isAdded && (
-
- )}
{record.expanded && (