From 0d8e6501d3cdb0943c88c7df9595fe04e538c218 Mon Sep 17 00:00:00 2001 From: ValueOn AG Date: Tue, 12 May 2026 21:31:27 +0200 Subject: [PATCH] teamsbot auth fixes --- .../FormGeneratorTree/FormGeneratorTree.tsx | 28 ++++++--- src/components/UnifiedDataBar/SourcesTab.tsx | 60 ++++++++++--------- src/pages/views/teamsbot/Teamsbot.module.css | 11 ++++ .../views/teamsbot/TeamsbotSessionView.tsx | 53 +++++++++------- 4 files changed, 95 insertions(+), 57 deletions(-) diff --git a/src/components/FormGenerator/FormGeneratorTree/FormGeneratorTree.tsx b/src/components/FormGenerator/FormGeneratorTree/FormGeneratorTree.tsx index fa1087c..60b08ca 100644 --- a/src/components/FormGenerator/FormGeneratorTree/FormGeneratorTree.tsx +++ b/src/components/FormGenerator/FormGeneratorTree/FormGeneratorTree.tsx @@ -58,6 +58,13 @@ function _buildChildMap(nodes: TreeNode[]): Map { + if (a.type === 'folder' && b.type !== 'folder') return -1; + if (a.type !== 'folder' && b.type === 'folder') return 1; + return a.name.localeCompare(b.name, undefined, { sensitivity: 'base' }); + }); + } return map; } @@ -651,10 +658,14 @@ export function FormGeneratorTree({ const _handleCycleScope = useCallback( async (node: TreeNode) => { const newScope = _nextScope(node.scope); - await provider.patchScope?.([node.id], newScope); - setNodes((prev) => - prev.map((n) => (n.id === node.id ? { ...n, scope: newScope } : n)), - ); + const isFolder = node.type === 'folder'; + await provider.patchScope?.([node.id], newScope, isFolder); + setNodes((prev) => { + if (!isFolder) return prev.map((n) => (n.id === node.id ? { ...n, scope: newScope } : n)); + const descendantIds = new Set(_collectDescendantIds(node.id, prev)); + descendantIds.add(node.id); + return prev.map((n) => descendantIds.has(n.id) ? { ...n, scope: newScope } : n); + }); }, [provider], ); @@ -663,9 +674,12 @@ export function FormGeneratorTree({ async (node: TreeNode) => { const newValue = !node.neutralize; await provider.patchNeutralize?.([node.id], newValue); - setNodes((prev) => - prev.map((n) => (n.id === node.id ? { ...n, neutralize: newValue } : n)), - ); + setNodes((prev) => { + if (node.type !== 'folder') return prev.map((n) => (n.id === node.id ? { ...n, neutralize: newValue } : n)); + const descendantIds = new Set(_collectDescendantIds(node.id, prev)); + descendantIds.add(node.id); + return prev.map((n) => descendantIds.has(n.id) ? { ...n, neutralize: newValue } : n); + }); }, [provider], ); diff --git a/src/components/UnifiedDataBar/SourcesTab.tsx b/src/components/UnifiedDataBar/SourcesTab.tsx index e287097..4d2ccf4 100644 --- a/src/components/UnifiedDataBar/SourcesTab.tsx +++ b/src/components/UnifiedDataBar/SourcesTab.tsx @@ -343,7 +343,7 @@ async function _loadServices(instanceId: string, connectionId: string): Promise< service: s.service, path: '/', displayPath: s.label || s.service, - })); + })).sort((a: TreeNode, b: TreeNode) => a.label.localeCompare(b.label, undefined, { sensitivity: 'base' })); } async function _browseService( @@ -375,6 +375,10 @@ async function _browseService( path: entry.path, displayPath, }; + }).sort((a: TreeNode, b: TreeNode) => { + if (a.type === 'folder' && b.type !== 'folder') return -1; + if (a.type !== 'folder' && b.type === 'folder') return 1; + return a.label.localeCompare(b.label, undefined, { sensitivity: 'base' }); }); } @@ -496,6 +500,7 @@ const SourcesTab: React.FC = ({ context, onSourcesChanged, onSe scope: d.scope || 'personal', neutralize: d.neutralize ?? false, })); + list.sort((a, b) => a.label.localeCompare(b.label, undefined, { sensitivity: 'base' })); setDataSources(list); }) .catch(() => { if (mountedRef.current) setDataSources([]); }); @@ -519,6 +524,7 @@ const SourcesTab: React.FC = ({ context, onSourcesChanged, onSe neutralizeFields: d.neutralizeFields || undefined, recordFilter: d.recordFilter || undefined, })); + list.sort((a, b) => a.label.localeCompare(b.label, undefined, { sensitivity: 'base' })); setFeatureDataSources(list); }) .catch(() => { if (mountedRef.current) setFeatureDataSources([]); }); @@ -547,7 +553,8 @@ const SourcesTab: React.FC = ({ context, onSourcesChanged, onSe children: null, connectionId: c.id, authority: c.authority, - })); + })) + .sort((a, b) => a.label.localeCompare(b.label, undefined, { sensitivity: 'base' })); setTree(nodes); }) .catch(() => { if (mountedRef.current) setTree([]); }) @@ -760,7 +767,7 @@ const SourcesTab: React.FC = ({ context, onSourcesChanged, onSe expanded: false, loading: false, tables: null, - })), + })).sort((a: FeatureConnectionNode, b: FeatureConnectionNode) => a.label.localeCompare(b.label, undefined, { sensitivity: 'base' })), }))); }) .catch(() => { if (mountedRef.current) setFeatureTree([]); }) @@ -798,14 +805,14 @@ const SourcesTab: React.FC = ({ context, onSourcesChanged, onSe objectKey: t.objectKey ?? '', tableName: t.tableName ?? '', label: t.label ?? '', - fields: t.fields ?? [], + fields: (t.fields ?? []).slice().sort((a: string, b: string) => a.localeCompare(b, undefined, { sensitivity: 'base' })), isParent: Boolean(t.isParent), parentTable: t.parentTable ?? null, parentKey: t.parentKey ?? null, displayFields: t.displayFields ?? [], isGroup: Boolean(t.isGroup), group: t.group ?? null, - })); + })).sort((a: FeatureTableNode, b: FeatureTableNode) => (a.label || a.tableName).localeCompare(b.label || b.tableName, undefined, { sensitivity: 'base' })); // Default-expand all categorical groups so users immediately see their content. const defaultExpansions: string[] = tables @@ -912,7 +919,7 @@ const SourcesTab: React.FC = ({ context, onSourcesChanged, onSe displayLabel: r.displayLabel || r.id, fields: r.fields || {}, tableName: table.tableName, - })); + })).sort((a: ParentRecordNode, b: ParentRecordNode) => a.displayLabel.localeCompare(b.displayLabel, undefined, { sensitivity: 'base' })); if (mountedRef.current) { setFeatureRecordsByPath(prev => ({ ...prev, [pathKey]: records })); } @@ -1229,11 +1236,24 @@ const _TreeNodeView: React.FC<_TreeNodeViewProps> = ({ {node.label} - {/* ── Stable trio: chat | scope | neutralize (always in this order). - * No "remove from workspace" button here by design: the UDB row only - * exposes the catalog state. Detach from the *current chat* happens - * via the chip "x" in WorkspaceInput; that chip is the single source - * of truth for chat-scoped attachment lifecycle. */} + - diff --git a/src/pages/views/teamsbot/Teamsbot.module.css b/src/pages/views/teamsbot/Teamsbot.module.css index a096bdf..eb06248 100644 --- a/src/pages/views/teamsbot/Teamsbot.module.css +++ b/src/pages/views/teamsbot/Teamsbot.module.css @@ -415,6 +415,17 @@ padding: 1rem; } +.sessionSwitcherSelect { + width: 100%; + max-width: 420px; + padding: 0.5rem 0.75rem; + font-size: 0.85rem; + border: 1px solid var(--border-color, #ccc); + border-radius: 8px; + background: var(--bg-primary, #fff); + color: var(--text-primary, #333); +} + /* ----- Session Layout (UDB Sidebar + Main) ------------------------------- */ .sessionLayout { diff --git a/src/pages/views/teamsbot/TeamsbotSessionView.tsx b/src/pages/views/teamsbot/TeamsbotSessionView.tsx index 67ba6af..5de3056 100644 --- a/src/pages/views/teamsbot/TeamsbotSessionView.tsx +++ b/src/pages/views/teamsbot/TeamsbotSessionView.tsx @@ -771,28 +771,39 @@ export const TeamsbotSessionView: React.FC = () => { )} - {/* Session Switcher (if multiple sessions exist) */} + {/* Session Switcher */} {allSessions.length > 1 && ( -
- {allSessions.map((s) => ( - - ))} +
+
)}